Compare commits
174 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 843418ae70 | |||
| 0ec8c56567 | |||
| 9101b90159 | |||
| f63bc3a922 | |||
| 58a4e08e3d | |||
| d0056d4234 | |||
| c4190338c5 | |||
| eeb38d3665 | |||
| 946857cfee | |||
| 2fcc13f8c9 | |||
| 494811810c | |||
| c7d155aa5d | |||
| f4197be567 | |||
| 081a877211 | |||
| a5e454d11c | |||
| 725e6836d0 | |||
| 5b19de37b6 | |||
| 794a9b48ac | |||
| 873cd21ec3 | |||
| ac16642cc4 | |||
| 40ce52a4e5 | |||
| 1073985be7 | |||
| 980580efec | |||
| a68046e79d | |||
| 6d62ed4f87 | |||
| df3fca8da4 | |||
| 3df99c4861 | |||
| d7e7ab7b5f | |||
| 6cb88552bc | |||
| 325a361d5a | |||
| c23a1b9bea | |||
| 57b61bf78f | |||
| 3a4c9daa5c | |||
| b58ccaea8a | |||
| 7423111fe1 | |||
| ec047587e3 | |||
| f1928b3e54 | |||
| f639befec8 | |||
| 4b43acc718 | |||
| 9e91a1fe89 | |||
| 9432655038 | |||
| fef2233090 | |||
| d26ac414c8 | |||
| ef4abc0a63 | |||
| 847d2ed685 | |||
| 07e071e500 | |||
| 53794ab541 | |||
| 234d2e51ac | |||
| 02268693c3 | |||
| 4d91527aa5 | |||
| 1db7790ecb | |||
| e20406040a | |||
| 4dc404cef4 | |||
| f4870a2b51 | |||
| 36a54c001e | |||
| 4e12c0b0e9 | |||
| 1d06c9f700 | |||
| 3b2754446a | |||
| dd3b30ec85 | |||
| 18c7b4d651 | |||
| 3338ca5f56 | |||
| 7a0d5bdd29 | |||
| 850da77de1 | |||
| c2b9896e50 | |||
| 9a3561e2d9 | |||
| c5d6fbdeea | |||
| 8254c1a4fa | |||
| 02751a7162 | |||
| a7b9a10707 | |||
| d5ee8aa625 | |||
| e85b670a55 | |||
| ce4a86e08f | |||
| 0412f3297a | |||
| c4f198bfc4 | |||
| c37fcdd5d1 | |||
| bf891f707b | |||
| 2a376849ca | |||
| b9a471a745 | |||
| a9136b45fa | |||
| ed72bb3fa3 | |||
| 3b847a4040 | |||
| b43e8920d8 | |||
| 0a416b9fd9 | |||
| 7f947dbd6c | |||
| 3a5f3aa858 | |||
| babac214d6 | |||
| 71b65c5055 | |||
| 0c4c0dbc7d | |||
| dfeb788b0f | |||
| a04f5335f9 | |||
| 57781916ad | |||
| e029b4a44e | |||
| 09f16c5ab6 | |||
| f863c0187c | |||
| e2201e40ce | |||
| 7f28c6d64e | |||
| c1aac60386 | |||
| 639d8b407b | |||
| 3e98ff6b1d | |||
| edc34bf866 | |||
| bfa8c7d370 | |||
| 44aea46a07 | |||
| 96e47e16e7 | |||
| 22d6a48ba7 | |||
| ca111b06f3 | |||
| e8cd0ee61f | |||
| 6eb5f5d9a7 | |||
| 39d132db5b | |||
| ec27a2e7e5 | |||
| d2424338b0 | |||
| a6b4039050 | |||
| eedf226700 | |||
| 84c03d6c36 | |||
| b6dcd0ae00 | |||
| 10747754d4 | |||
| 78e831d31f | |||
| 04586681af | |||
| 099f7f052b | |||
| 1f3efba202 | |||
| cb69edd23d | |||
| bac6b5eaef | |||
| 2e660d30f5 | |||
| 1301bc1f6e | |||
| 33cc175e51 | |||
| b230147671 | |||
| a354a15717 | |||
| 0296bab9fd | |||
| 9ff8a73ba4 | |||
| 35cc5176e0 | |||
| 03e6242802 | |||
| 630b111cd3 | |||
| a73fb2b233 | |||
| ccae8e0c47 | |||
| 74e5918178 | |||
| c64739e017 | |||
| a75737f931 | |||
| c873460fd1 | |||
| 52d1e3aef3 | |||
| cc1a3d3d85 | |||
| 09efb92900 | |||
| b3a3a4c006 | |||
| fa56585242 | |||
| a678784c4c | |||
| e40b5d9867 | |||
| f32be465e8 | |||
| 3c11df7e92 | |||
| 21769e1d85 | |||
| 41b5ded8d0 | |||
| 22f8a84fda | |||
| ce3d809d62 | |||
| dcd722d204 | |||
| 15a064807f | |||
| 8e544ca512 | |||
| 8c8bd1969c | |||
| 861cb4c0b4 | |||
| 6e334ef616 | |||
| a1f32824e2 | |||
| 5b5b35f6d6 | |||
| a0b13abd34 | |||
| 377c4a8bfe | |||
| 7ebd8a8d85 | |||
| f39a16c595 | |||
| c02f8fdfe2 | |||
| 064a1aa308 | |||
| 9abd7edaa7 | |||
| d802904c8a | |||
| c592b7e01d | |||
| 1f6d80c1e2 | |||
| d7330b571d | |||
| fc66c6380f | |||
| eceff127ea | |||
| d47220c190 | |||
| 32c49bd6e6 | |||
| ba1ee803ee |
+22
-10
@@ -1459,7 +1459,7 @@ SRC01 = \
|
||||
$(TOP)\src\status.c \
|
||||
$(TOP)\src\table.c \
|
||||
$(TOP)\src\threads.c \
|
||||
$(TOP)\src\tclsqlite.c \
|
||||
tclsqlite-ex.c \
|
||||
$(TOP)\src\tokenize.c \
|
||||
$(TOP)\src\treeview.c \
|
||||
$(TOP)\src\trigger.c \
|
||||
@@ -2020,8 +2020,18 @@ sqlite3.c: .target_source sqlite3ext.h sqlite3session.h $(MKSQLITE3C_TOOL) src-v
|
||||
|
||||
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl $(JIM_TCLSH)
|
||||
$(JIM_TCLSH) $(TOP)\tool\split-sqlite3c.tcl
|
||||
|
||||
TCLSQLITEEX = \
|
||||
$(TOP)\ext\qrf\qrf.h \
|
||||
$(TOP)\ext\qrf\qrf.c \
|
||||
$(TOP)\src\tclsqlite.c
|
||||
|
||||
tclsqlite-ex.c: $(TCLSQLITEEX) $(TOP)\tool\mkcombo.tcl $(JIM_TCLSH)
|
||||
$(JIM_TCLSH) $(TOP)\tool\mkcombo.tcl $(TCLSQLITEEX) -o $@
|
||||
# <</mark>>
|
||||
|
||||
tclsqlite-ex.c:
|
||||
|
||||
# Rule to build the amalgamation
|
||||
#
|
||||
sqlite3.lo: $(SQLITE3C)
|
||||
@@ -2321,11 +2331,11 @@ whereexpr.lo: $(TOP)\src\whereexpr.c $(HDR)
|
||||
window.lo: $(TOP)\src\window.c $(HDR)
|
||||
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\window.c
|
||||
|
||||
tclsqlite.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
|
||||
tclsqlite.lo: tclsqlite-ex.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c tclsqlite-ex.c /OUT:tclsqlite.lo
|
||||
|
||||
tclsqlite-shell.lo: $(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DTCLSH -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
|
||||
tclsqlite-shell.lo: tclsqlite-ex.c $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTCOMPILE) $(NO_WARN) -DTCLSH -DBUILD_sqlite -I$(TCLINCDIR) -c tclsqlite-ex.c
|
||||
|
||||
tclsqlite3.exe: tclsqlite-shell.lo $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
|
||||
$(LTLINK) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) /OUT:$@ tclsqlite-shell.lo $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
@@ -2378,6 +2388,8 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
|
||||
# Source and header files that shell.c depends on
|
||||
SHELL_DEP = \
|
||||
$(TOP)\src\shell.c.in \
|
||||
$(TOP)\ext\qrf\qrf.c \
|
||||
$(TOP)\ext\qrf\qrf.h \
|
||||
$(TOP)\ext\expert\sqlite3expert.c \
|
||||
$(TOP)\ext\expert\sqlite3expert.h \
|
||||
$(TOP)\ext\intck\sqlite3intck.c \
|
||||
@@ -2541,9 +2553,9 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_STRICT_SUBTYPE=1
|
||||
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2)
|
||||
TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C)
|
||||
!IF $(USE_AMALGAMATION)==0
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
|
||||
TESTFIXTURE_SRC = $(TESTSRC) tclsqlite-ex.c $(TESTFIXTURE_SRC0)
|
||||
!ELSE
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC1)
|
||||
TESTFIXTURE_SRC = $(TESTSRC) tclsqlite-ex.c $(TESTFIXTURE_SRC1)
|
||||
!ENDIF
|
||||
|
||||
!IF $(USE_STDCALL)!=0 || $(FOR_WIN10)!=0
|
||||
@@ -2678,14 +2690,14 @@ smoketest: $(TESTPROGS)
|
||||
shelltest: $(TESTPROGS)
|
||||
.\testfixture.exe $(TOP)\test\permutations.test shell
|
||||
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE_TCL_DEP)
|
||||
sqlite3_analyzer.c: $(SQLITE3C) $(SQLITE3H) tclsqlite-ex.c $(TOP)\tool\spaceanal.tcl $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqlite3_analyzer.c.in $(TOP)\ext\misc\sqlite3_stdio.h $(TOP)\ext\misc\sqlite3_stdio.c $(SQLITE_TCL_DEP)
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl -DINCLUDE_SQLITE3_C $(TOP)\tool\sqlite3_analyzer.c.in > $@
|
||||
|
||||
sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
sqltclsh.c: sqlite3.c $(TOP)\src\tclsqlite.c $(TOP)\tool\sqltclsh.tcl $(TOP)\ext\misc\appendvfs.c $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in
|
||||
sqltclsh.c: sqlite3.c tclsqlite-ex.c $(TOP)\tool\sqltclsh.tcl $(TOP)\ext\misc\appendvfs.c $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in >sqltclsh.c
|
||||
|
||||
sqltclsh.exe: sqltclsh.c $(SHELL_CORE_DEP) $(LIBRESOBJS)
|
||||
@@ -2698,7 +2710,7 @@ sqlite3_expert.exe: $(SQLITE3C) $(TOP)\ext\expert\sqlite3expert.h $(TOP)\ext\exp
|
||||
CHECKER_DEPS =\
|
||||
$(TOP)\tool\mkccode.tcl \
|
||||
sqlite3.c \
|
||||
$(TOP)\src\tclsqlite.c \
|
||||
tclsqlite-ex.c \
|
||||
$(TOP)\ext\repair\sqlite3_checker.tcl \
|
||||
$(TOP)\ext\repair\checkindex.c \
|
||||
$(TOP)\ext\repair\checkfreelist.c \
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -805,7 +805,8 @@ proc sqlite-handle-common-feature-flags {} {
|
||||
sqlite-add-feature-flag -DSQLITE_ENABLE_MEMSYS3
|
||||
}
|
||||
}
|
||||
scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {}
|
||||
bytecode-vtab -DSQLITE_ENABLE_BYTECODE_VTAB {}
|
||||
scanstatus {-DSQLITE_ENABLE_STMT_SCANSTATUS -DSQLITE_ENABLE_BYTECODE_VTAB} {}
|
||||
column-metadata -DSQLITE_ENABLE_COLUMN_METADATA {}
|
||||
dbpage -DSQLITE_ENABLE_DBPAGE_VTAB {}
|
||||
dbstat -DSQLITE_ENABLE_DBSTAT_VTAB {}
|
||||
|
||||
+22
-8
@@ -696,6 +696,7 @@ other than that, the order of directives in Lemon is arbitrary.</p>
|
||||
<li><tt><a href='#pright'>%right</a></tt>
|
||||
<li><tt><a href='#reallc'>%realloc</a></tt>
|
||||
<li><tt><a href='#stack_overflow'>%stack_overflow</a></tt>
|
||||
<li><tt><a href='#reallc'>%stack_size_limit</a></tt>
|
||||
<li><tt><a href='#stack_size'>%stack_size</a></tt>
|
||||
<li><tt><a href='#start_symbol'>%start_symbol</a></tt>
|
||||
<li><tt><a href='#syntax_error'>%syntax_error</a></tt>
|
||||
@@ -1203,20 +1204,33 @@ the wildcard token and some other token, the other token is always used.
|
||||
The wildcard token is only matched if there are no alternatives.</p>
|
||||
|
||||
<a id='reallc'></a>
|
||||
<h4>4.4.26 The <tt>%realloc</tt> and <tt>%free</tt> directives</h4>
|
||||
<h4>4.4.26 The <tt>%realloc</tt>, <tt>%free</tt>, and
|
||||
<tt>%stack_size_limit</tt> directives</h4>
|
||||
|
||||
<p>The <tt>%realloc</tt> and <tt>%free</tt> directives defines function
|
||||
that allocate and free heap memory. The signatures of these functions
|
||||
should be the same as the realloc() and free() functions from the standard
|
||||
C library.
|
||||
that allocate and free heap memory. The signatures and semantics of
|
||||
these functions are similar to the realloc() and free() functions from
|
||||
the standard C library, except that these functions take an extra
|
||||
parameter at the end that is determined by %extra_context. If
|
||||
%extra_context is not defined, then the extra argument is 0. The
|
||||
extra parameter provides the capability to do better error reporting
|
||||
in the event of a memory allocation error, and/or to use an alternative
|
||||
private application heap.
|
||||
|
||||
<p>If both of these functions are defined
|
||||
then these functions are used to allocate and free
|
||||
memory for supplemental parser stack space, if the initial
|
||||
parse stack space is exceeded. The initial parser stack size
|
||||
<p>If both of these functions are defined then they are used to
|
||||
allocate and free memory for supplemental parser stack space, if
|
||||
the initial parse stack space is exceeded. The initial parser stack size
|
||||
is specified by either <tt>%stack_size</tt> or the
|
||||
-DYYSTACKDEPTH compile-time flag.
|
||||
|
||||
<p>The <tt>%stack_size_limit</tt> directive defines a function that returns
|
||||
the maximum allowed parser stack size. If this diretive does not exist,
|
||||
no size limit is enforced. This function takes a single argument which
|
||||
is the %extra_context value or "0" if %extra_context is not defined.
|
||||
The function should return an integer that is the maximum
|
||||
number of parser stack entries. If more stack space
|
||||
than this is needed, the %stack_overflow code is invoked.
|
||||
|
||||
<a id='errors'></a>
|
||||
<h2>5.0 Error Processing</h2>
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ foreach {tn setup} {
|
||||
proc do_rec_test {tn sql res} {
|
||||
set res [squish [string trim $res]]
|
||||
set tst [subst -nocommands {
|
||||
squish [string trim [exec $::CLI test.db ".expert" {$sql;}]]
|
||||
squish [string trim [exec $::CLI -noinit test.db ".expert" {$sql;}]]
|
||||
}]
|
||||
uplevel [list do_test $tn $tst $res]
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ int sqlite3_fputs(const char *z, FILE *out){
|
||||
|
||||
|
||||
/*
|
||||
** Work-alike for fprintf() from the standard C library.
|
||||
** Work-alikes for fprintf() and vfprintf() from the standard C library.
|
||||
*/
|
||||
int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
|
||||
int rc;
|
||||
@@ -285,6 +285,24 @@ int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
int sqlite3_vfprintf(FILE *out, const char *zFormat, va_list ap){
|
||||
int rc;
|
||||
if( UseWtextForOutput(out) ){
|
||||
/* When writing to the command-prompt in Windows, it is necessary
|
||||
** to use _O_WTEXT input mode and write UTF-16 characters.
|
||||
*/
|
||||
char *z;
|
||||
z = sqlite3_vmprintf(zFormat, ap);
|
||||
sqlite3_fputs(z, out);
|
||||
rc = (int)strlen(z);
|
||||
sqlite3_free(z);
|
||||
}else{
|
||||
/* Writing to a file or other destination, just write bytes without
|
||||
** any translation. */
|
||||
rc = vfprintf(out, zFormat, ap);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the mode for an output stream. mode argument is typically _O_BINARY or
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#ifdef _WIN32
|
||||
/**** Definitions For Windows ****/
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <windows.h>
|
||||
|
||||
FILE *sqlite3_fopen(const char *zFilename, const char *zMode);
|
||||
@@ -38,6 +39,7 @@ FILE *sqlite3_popen(const char *zCommand, const char *type);
|
||||
char *sqlite3_fgets(char *s, int size, FILE *stream);
|
||||
int sqlite3_fputs(const char *s, FILE *stream);
|
||||
int sqlite3_fprintf(FILE *stream, const char *format, ...);
|
||||
int sqlite3_vfprintf(FILE *stream, const char *format, va_list);
|
||||
void sqlite3_fsetmode(FILE *stream, int mode);
|
||||
|
||||
|
||||
@@ -49,6 +51,7 @@ void sqlite3_fsetmode(FILE *stream, int mode);
|
||||
#define sqlite3_fgets fgets
|
||||
#define sqlite3_fputs fputs
|
||||
#define sqlite3_fprintf fprintf
|
||||
#define sqlite3_vfprintf vfprintf
|
||||
#define sqlite3_fsetmode(F,X) /*no-op*/
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,674 @@
|
||||
# SQLite Query Result Formatting Subsystem
|
||||
|
||||
The "Query Result Formatter" or "QRF" subsystem is a C-language
|
||||
subroutine that formats the output from an SQLite query for display using
|
||||
a fix-width font, for example on a terminal window over an SSH connection.
|
||||
The output format is configurable. The application can request various
|
||||
table formats, with flexible column widths and alignments, row-oriented
|
||||
formats, such as CSV and similar, as well as various special purpose formats
|
||||
like JSON.
|
||||
|
||||
For the first 25 years of SQLite's existance, the
|
||||
[command-line interface](https://sqlite.org/cli.html) (CLI)
|
||||
formatted query results using a hodge-podge of routines
|
||||
that had grown slowly by accretion. The QRF was created
|
||||
in fall of 2025 to refactor and reorganize this code into
|
||||
a more usable form. The idea behind QRF is to implement all the
|
||||
query result formatting capabilities of the CLI in a subroutine
|
||||
that can be incorporated and reused by other applications.
|
||||
|
||||
## 1.0 Overview Of Operation
|
||||
|
||||
Suppose `pStmt` is a pointer to an SQLite prepared statement
|
||||
(a pointer to an `sqlite3_stmt` object) that has been reset and
|
||||
bound and is ready to run. Then to format the output from this
|
||||
prepared statement, use code similar to the following:
|
||||
|
||||
> ~~~
|
||||
sqlite3_qrf_spec spec; /* Format specification */
|
||||
char *zErrMsg; /* Text error message (optional) */
|
||||
char *zResult = 0; /* Formatted output written here */
|
||||
int rc; /* Result code */
|
||||
|
||||
memset(&spec, 0, sizeof(spec)); /* Initialize the spec */
|
||||
spec.iVersion = 1; /* Version number must be 1 */
|
||||
spec.pzOutput = &zResult; /* Write results in variable zResult */
|
||||
/* Optionally fill in other settings in spec here, as needed */
|
||||
zErrMsg = 0; /* Not required; just being pedantic */
|
||||
rc = sqlite3_format_query_result(pStmt, &spec, &zErrMsg); /* Format results */
|
||||
if( rc ){
|
||||
printf("Error (%d): %s\n", rc, zErrMsg); /* Report an error */
|
||||
sqlite3_free(zErrMsg); /* Free the error message text */
|
||||
}else{
|
||||
printf("%s", zResult); /* Report the results */
|
||||
sqlite3_free(zResult); /* Free memory used to hold results */
|
||||
}
|
||||
~~~
|
||||
|
||||
The `sqlite3_qrf_spec` object describes the desired output format
|
||||
and what to do with the generated output. Most of the work in using
|
||||
the QRF involves filling out the sqlite3_qrf_spec.
|
||||
|
||||
## 2.0 The `sqlite3_qrf_spec` object
|
||||
|
||||
The `sqlite3_qrf_spec` structure defines how the results of a query
|
||||
are to be formatted, and what to do with the formatted text. The
|
||||
most recent definition of `sqlite3_qrf_spec` is shown below.
|
||||
|
||||
Do not be alarmed by the complexity of this structure. You only have
|
||||
to understand the properties you want to modify. Zero is always a good
|
||||
default for all of the attributes (except iVersion and pzOutput/xWrite)
|
||||
and so simply zeroing out the bulk of this structure is a good start.
|
||||
You can then slowly make adjustments to individual fields to get the
|
||||
results you desire.
|
||||
|
||||
> ~~~
|
||||
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
|
||||
struct sqlite3_qrf_spec {
|
||||
unsigned char iVersion; /* Version number of this structure */
|
||||
unsigned char eStyle; /* Formatting style. "box", "csv", etc... */
|
||||
unsigned char eEsc; /* How to escape control characters in text */
|
||||
unsigned char eText; /* Quoting style for text */
|
||||
unsigned char eTitle; /* Quating style for the text of column names */
|
||||
unsigned char eBlob; /* Quoting style for BLOBs */
|
||||
unsigned char bTitles; /* True to show column names */
|
||||
unsigned char bWordWrap; /* Try to wrap on word boundaries */
|
||||
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
|
||||
unsigned char bTextNull; /* Apply eText encoding to zNull[] */
|
||||
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
|
||||
unsigned char eTitleAlign; /* Alignment for column headers */
|
||||
short int nWrap; /* Wrap columns wider than this */
|
||||
short int nScreenWidth; /* Maximum overall table width */
|
||||
short int nLineLimit; /* Maximum number of lines for any row */
|
||||
int nCharLimit; /* Maximum number of characters in a cell */
|
||||
int nWidth; /* Number of entries in aWidth[] */
|
||||
int nAlign; /* Number of entries in aAlignment[] */
|
||||
short int *aWidth; /* Column widths */
|
||||
unsigned char *aAlign; /* Column alignments */
|
||||
char *zColumnSep; /* Alternative column separator */
|
||||
char *zRowSep; /* Alternative row separator */
|
||||
char *zTableName; /* Output table name */
|
||||
char *zNull; /* Rendering of NULL */
|
||||
char *(*xRender)(void*,sqlite3_value*); /* Render a value */
|
||||
int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */
|
||||
void *pRenderArg; /* First argument to the xRender callback */
|
||||
void *pWriteArg; /* First argument to the xWrite callback */
|
||||
char **pzOutput; /* Storage location for output string */
|
||||
/* Additional fields may be added in the future */
|
||||
};
|
||||
~~~
|
||||
|
||||
Again, the only fields that must initialized are:
|
||||
|
||||
* `.iVersion`
|
||||
* One of `.pzOutput` or `.xWrite`.
|
||||
|
||||
All other fields can be zeroed. Or they can contain other values to
|
||||
alter the formatting of the query results.
|
||||
|
||||
Further detail on the meanings of each of the fields in the
|
||||
`sqlite3_qrf_spec` object are described below.
|
||||
|
||||
### 2.1 Structure Version Number
|
||||
|
||||
The sqlite3_qrf_spec.iVersion field must be 1. Future enhancements to
|
||||
the QRF might add new fields onto the bottom of the sqlite3_qrf_spec
|
||||
object. Those new fields will only be accessible if the iVersion is greater
|
||||
than 1. Thus the iVersion field is used to support upgradability.
|
||||
|
||||
### 2.2 Output Deposition (xWrite and pzOutput)
|
||||
|
||||
The formatted output can either be sent to a callback function
|
||||
or accumulated into an output buffer in memory obtained
|
||||
from sqlite3_malloc(). If the sqlite3_qrf_spec.xWrite column is not NULL,
|
||||
then that function is invoked (using sqlite3_qrf_spec.xWriteArg as its
|
||||
first argument) to transmit the formatted output. Or, if
|
||||
sqlite3_qrf_spec.pzOutput points to a pointer to a character, then that
|
||||
pointer is made to point to memory obtained from sqlite3_malloc() that
|
||||
contains the complete text of the formatted output. If spec.pzOutput\[0\]
|
||||
is initially non-NULL, then it is assumed to point to memory obtained
|
||||
from sqlite3_malloc(). In that case, the buffer is resized using
|
||||
sqlite3_realloc() and the new text is appended.
|
||||
|
||||
One of either sqlite3_qrf_spec.xWrite and sqlite3_qrf_spec.pzOutput must be
|
||||
non-NULL and the other must be NULL.
|
||||
|
||||
The return value from xWrite is an SQLITE result code. The usual return
|
||||
should be SQLITE_OK. But if for some reason the write fails, a different
|
||||
value might be returned.
|
||||
|
||||
### 2.3 Output Format
|
||||
|
||||
The sqlite3_qrf_spec.eStyle field is an integer code that defines the
|
||||
specific output format that will be generated. See section 4.0 below
|
||||
for details on the meaning of the various style options.
|
||||
|
||||
Other fields in sqlite3_qrf_spec might be used or might be
|
||||
ignored, depending on the value of eStyle.
|
||||
|
||||
### 2.4 Show Column Names (bTitles)
|
||||
|
||||
The sqlite3_qrf_spec.bTitles field can be either QRF_SW_Auto,
|
||||
QRF_SW_On, or QRF_SW_Off. Those three constants also have shorter
|
||||
alternative spellings: QRF_Auto, QRF_No, and
|
||||
QRF_Yes.
|
||||
|
||||
> ~~~
|
||||
#define QRF_SW_Auto 0 /* Let QRF choose the best value */
|
||||
#define QRF_SW_Off 1 /* This setting is forced off */
|
||||
#define QRF_SW_On 2 /* This setting is forced on */
|
||||
#define QRF_Auto 0 /* Alternate spelling for QRF_SW_Auto and others */
|
||||
#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */
|
||||
#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */
|
||||
~~~
|
||||
|
||||
If the value is QRF_Yes, then column names appear in the output.
|
||||
If the value is QRF_No, column names are omitted. If the
|
||||
value is QRF_Auto, then an appropriate default is chosen.
|
||||
|
||||
### 2.5 Control Character Escapes (eEsc)
|
||||
|
||||
The sqlite3_qrf_spec.eEsc determines how ASCII control characters are
|
||||
formatted when displaying TEXT values in the result. These are the allowed
|
||||
values:
|
||||
|
||||
> ~~~
|
||||
#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */
|
||||
#define QRF_ESC_Off 1 /* Do not escape control characters */
|
||||
#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */
|
||||
#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */
|
||||
~~~
|
||||
|
||||
If the value of eEsc is QRF_ESC_Ascii, then the control character
|
||||
with value X is displayed as ^Y where Y is X+0x40. Hence, a
|
||||
backspace character (U+0008) is shown as "^H".
|
||||
|
||||
If eEsc is QRF_ESC_Symbol, then control characters in the range of U+0001
|
||||
through U+001f are mapped into U+2401 through U+241f, respectively.
|
||||
|
||||
If the value of eEsc is QRF_ESC_Off, then no translation occurs
|
||||
and control characters that appear in TEXT strings are transmitted
|
||||
to the formatted output as-is. This can be dangerous in applications,
|
||||
since an adversary who can control TEXT values might be able to
|
||||
inject ANSI cursor movement sequences to hide nefarious values.
|
||||
|
||||
The QRF_ESC_Auto value for eEsc means that the query result formatter
|
||||
gets to pick whichever control-character encoding it thinks is best for
|
||||
the situation. This will usually be QRF_ESC_Ascii.
|
||||
|
||||
The TAB (U+0009), LF (U+000a) and CR-LF (U+000d,U+000a) character
|
||||
sequence are always output literally and are not mapped to alternative
|
||||
display values, regardless of this setting.
|
||||
|
||||
### 2.6 Display of TEXT values (eText, eTitle)
|
||||
|
||||
The sqlite3_qrf_spec.eText controls how text values are rendered in the
|
||||
display. sqlite3_qrf_spec.eTitle controls how column names are rendered.
|
||||
Both fields can have one of the following values:
|
||||
|
||||
> ~~~
|
||||
#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */
|
||||
#define QRF_TEXT_Plain 1 /* Literal text */
|
||||
#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */
|
||||
#define QRF_TEXT_Csv 3 /* CSV-style quoting */
|
||||
#define QRF_TEXT_Html 4 /* HTML-style quoting */
|
||||
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
|
||||
#define QRF_TEXT_Json 6 /* JSON quoting */
|
||||
~~~
|
||||
|
||||
A value of QRF_TEXT_Auto means that the query result formatter will choose
|
||||
what it thinks will be the best text encoding.
|
||||
|
||||
A value of QRF_TEXT_Plain means that text values appear in the output exactly
|
||||
as they are found in the database file, with no translation.
|
||||
|
||||
A value of QRF_TEXT_Sql means that text values are escaped so that they
|
||||
look like SQL literals. That means the value will be surrounded by
|
||||
single-quotes (U+0027) and any single-quotes contained within the text
|
||||
will be doubled.
|
||||
|
||||
A value of QRF_TEXT_Csv means that text values are escaped in accordance
|
||||
with RFC 4180, which defines Comma-Separated-Value or CSV files.
|
||||
Text strings that contain no special values appears as-is. Text strings
|
||||
that contain special values are contained in double-quotes (U+0022) and
|
||||
any double-quotes within the value are doubled.
|
||||
|
||||
A value of QRF_TEXT_Html means that text values are escaped for use in
|
||||
HTML. Special characters "<", "&", ">", """, and "'"
|
||||
are displayed as "&lt;", "&amp;", "&gt;", "&quot;",
|
||||
and "&#39;", respectively.
|
||||
|
||||
A value of QRF_TEXT_Tcl means that text values are displayed inside of
|
||||
double-quotes and special characters within the string are escaped using
|
||||
backslash escape, as in ANSI-C or TCL or Perl or other popular programming
|
||||
languages.
|
||||
|
||||
A value of QRF_TEXT_Json gives similar results as QRF_TEXT_Tcl except that the
|
||||
rules are adjusted so that the displayed string is strictly conforming
|
||||
the JSON specification.
|
||||
|
||||
### 2.7 How to display BLOB values (eBlob and bTextJsonb)
|
||||
|
||||
If the sqlite3_qrf_spec.bTextJsonb flag is QRF_SW_On and if the value to be
|
||||
displayed is JSONB, then the JSONB is translated into text JSON and the
|
||||
text is shown according to the sqlite3_qrf_spec.eText setting as
|
||||
described in the previous section.
|
||||
|
||||
If the bTextJsonb flag is QRF_SW_Off (the usual case) or if the BLOB value to
|
||||
be displayed is not JSONB, then the sqlite3_qrf_spec.eBlob field determines
|
||||
how the BLOB value is formatted. The following options are available;
|
||||
|
||||
> ~~~
|
||||
#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */
|
||||
#define QRF_BLOB_Text 1 /* Display content exactly as it is */
|
||||
#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */
|
||||
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
|
||||
#define QRF_BLOB_Tcl 4 /* "\000" notation */
|
||||
#define QRF_BLOB_Json 5 /* A JSON string */
|
||||
~~~
|
||||
|
||||
A value of QRF_BLOB_Auto means that display format is selected automatically
|
||||
by sqlite3_format_query_result() based on eStyle and eText.
|
||||
|
||||
A value of QRF_BLOB_Text means that BLOB values are interpreted as UTF8
|
||||
text and are displayed using formatting results set by eEsc and
|
||||
eText.
|
||||
|
||||
A value of QRF_BLOB_Sql means that BLOB values are shown as SQL BLOB
|
||||
literals: a prefix "`x'`" following by hexadecimal and ending with a
|
||||
final "`'`".
|
||||
|
||||
A value of QRF_BLOB_Hex means that BLOB values are shown as
|
||||
hexadecimal text with no delimiters.
|
||||
|
||||
A value of QRF_BLOB_Tcl means that BLOB values are shown as a
|
||||
C/Tcl/Perl string literal where every byte is an octal backslash
|
||||
escape. So a BLOB of `x'052881f3'` would be displayed as
|
||||
`"\005\050\201\363"`.
|
||||
|
||||
A value of QRF_BLOB_Json is similar to QRF_BLOB_Tcl except that is
|
||||
uses unicode backslash escapes, since JSON does not understand
|
||||
the C/Tcl/Perl octal backslash escapes. So the string from the
|
||||
previous paragraph would be shown as
|
||||
`"\u0005\u0028\u0081\u00f3"`.
|
||||
|
||||
### 2.8 Maximum size of displayed content (nLineLimit, nCharLimit)
|
||||
|
||||
If the sqlite3_qrf_spec.nCharLimit setting is non-zero, then the formatter
|
||||
will display only the first nCharLimit characters of each value.
|
||||
Only characters that take up space are counted when enforcing this
|
||||
limit. Zero-width characters and VT100 escape sequences do not count
|
||||
toward this limit. The count is in characters, not bytes. When
|
||||
imposing this limit, the formatter adds the three characters "..."
|
||||
to the end of the value. Those added characters are not counted
|
||||
as part of the limit. Very small limits still result in truncation,
|
||||
but might render a few more characters than the limit.
|
||||
|
||||
If the sqlite3_qrf_spec.nLineLimit setting is non-zero, then the
|
||||
formatter will only display the first nLineLimit lines of each value.
|
||||
It does not matter if the value is split because it contains a newline
|
||||
character, or if it split by wrapping. This setting merely limits
|
||||
the number of displayed lines. This setting only works for
|
||||
**Box**, **Column**, **Line**, **Markdown**, and **Table** styles.
|
||||
|
||||
The idea behind both of these settings is to prevent large renderings
|
||||
when doing a query that (unexpectedly) contains very large text or
|
||||
blob values: perhaps megabyes of text.
|
||||
|
||||
### 2.9 Word Wrapping In Columnar Styles (nWrap, bWordWrap)
|
||||
|
||||
When using columnar formatting modes (QRF_STYLE_Box, QRF_STYLE_Column,
|
||||
QRF_STYLE_Markdown, or QRF_STYLE_Table), the formatter attempts to limit
|
||||
the width of any individual column to sqlite3_qrf_spec.nWrap characters
|
||||
if nWrap is non-zero. A zero value for nWrap means "unlimited".
|
||||
The nWrap limit might be exceeded if the limit is very small.
|
||||
|
||||
In order to keep individual columns within requested width limits,
|
||||
it is sometimes necessary to wrap the content for a single row of
|
||||
a single column across multiple lines. When this
|
||||
becomes necessary and if the bWordWrap setting is QRF_Yes, then the
|
||||
formatter attempts to split the content on whitespace or at a word boundary.
|
||||
If bWordWrap is QRF_No, then the formatter is free to split content
|
||||
anywhere, including in the middle of a word.
|
||||
|
||||
For narrow columns and wide words, it might sometimes be necessary to split
|
||||
a column in the middle of a word, even when bWordWrap is QRF_Yes.
|
||||
|
||||
### 2.10 Helping The Output To Fit On The Terminal (nScreenWidth)
|
||||
|
||||
The sqlite3_qrf_spec.nScreenWidth field can be set the number of
|
||||
characters that will fit on one line on the viewer output device.
|
||||
This is typically a number like 80 or 132. The formatter will attempt
|
||||
to reduce the length of output lines, depending on the style, so
|
||||
that all output fits on that screen.
|
||||
|
||||
A value of zero for nScreenWidth means "unknown" or "no width limit".
|
||||
When the value is zero, the formatter makes no attempt to keep the
|
||||
lines of output short.
|
||||
|
||||
The nScreenWidth is a hint to the formatter, not a requirement.
|
||||
The formatter trieds to keep lines below the nScreenWidth limit,
|
||||
but it does not guarantee that it will.
|
||||
|
||||
### 2.11 Individual Column Width (nWidth and aWidth)
|
||||
|
||||
The sqlite3_qrf_spec.aWidth field is a pointer to an array of
|
||||
signed 16-bit integers that control the width of individual columns
|
||||
in columnar output modes (QRF_STYLE_Box, QRF_STYLE_Column,
|
||||
QRF_STYLE_Markdown, or QRF_STYLE_Table). The sqlite3_qrf_spec.nWidth
|
||||
field is the number of integers in the aWidth array.
|
||||
|
||||
If aWidth is a NULL pointer or if nWidth is zero, then the array is
|
||||
assumed to be all zeros. If nWidth is less then the number of
|
||||
columns in the output, then zero is used for the width
|
||||
for all columns past then end of the aWidth array.
|
||||
|
||||
The aWidth array is deliberately an array of 16-bit signed integers.
|
||||
Only 16 bits are used because no good comes for having very large
|
||||
column widths. The range if further restricted as follows:
|
||||
|
||||
> ~~~
|
||||
#define QRF_MAX_WIDTH 10000 /* Maximum column width */
|
||||
#define QRF_MIN_WIDTH 0 /* Minimum column width */
|
||||
~~~
|
||||
|
||||
A width greater than then QRF_MAX_WIDTH is interpreted as QRF_MAX_WIDTH.
|
||||
|
||||
Any aWidth\[\] value of zero means the formatter should use a flexible
|
||||
width column (limited only by sqlite_qrf_spec.mxWidth) that is just
|
||||
big enough to hold the largest row.
|
||||
|
||||
For historical compatibility, aWidth\[\] can contain negative values,
|
||||
down to -QRF_MAX_WIDTH. The column width used is the absolute value
|
||||
of the number in aWidth\[\]. The only difference is that negative
|
||||
values cause the default horizontal alignment to be QRF_ALIGN_Right.
|
||||
The sign of the aWidth\[\] values only affects alignment if the
|
||||
alignment is not otherwise specified by aAlign\[\] or eDfltAlign.
|
||||
Again, negative values for aWidth\[\] entries are supported for
|
||||
backwards compatibility only, and are not recommended for new
|
||||
applications.
|
||||
|
||||
### 2.12 Alignment (nAlignment, aAlignment, eDfltAlign, eTitleAlign)
|
||||
|
||||
Some cells in a display table might contain a lot of text and thus
|
||||
be wide, or they might contain newline characters or be wrapped by
|
||||
width constraints so that they span many rows of text. Other cells
|
||||
might be narrower and shorter. In columnar formats, the display width
|
||||
of a cell is the maximum of the widest value in the same column, and the
|
||||
display height is the height of the tallest value in the same row.
|
||||
So some cells might be much taller and wider than necessary to hold
|
||||
their values.
|
||||
|
||||
Alignment determines where smaller values are placed within larger cells.
|
||||
|
||||
The sqlite3_qrf_spec.aAlign field points to an array of unsigned characters
|
||||
that specifies alignment (both vertical and horizontal) of individual
|
||||
columns within the table. The sqlite3_qrf_spec.nAlign fields holds
|
||||
the number of entries in the aAlign\[\] array.
|
||||
|
||||
If sqlite3_qrf_spec.aAlign is a NULL pointer or if sqlite3_qrf_spec.nAlign
|
||||
is zero, or for columns to the right of what are specified by
|
||||
sqlite3_qrf_spec.nAlign, the sqlite3_qrf_spec.eDfltAlign value is used
|
||||
for the alignment. Column names can be (and often are) aligned
|
||||
differently, as specified by sqlite3_qrf_spec.eTitleAlign.
|
||||
|
||||
Each alignment value specifies both vertical and horizontal alignment.
|
||||
Horizontal alignment can be left, center, right, or no preference.
|
||||
Vertical alignment can be top, middle, bottom, or no preference.
|
||||
Thus there are 16 possible alignment values, as follows:
|
||||
|
||||
> ~~~
|
||||
/*
|
||||
** Horizontal Vertial
|
||||
** ---------- -------- */
|
||||
#define QRF_ALIGN_Auto 0 /* auto auto */
|
||||
#define QRF_ALIGN_Left 1 /* left auto */
|
||||
#define QRF_ALIGN_Center 2 /* center auto */
|
||||
#define QRF_ALIGN_Right 3 /* right auto */
|
||||
#define QRF_ALIGN_Top 4 /* auto top */
|
||||
#define QRF_ALIGN_NW 5 /* left top */
|
||||
#define QRF_ALIGN_N 6 /* center top */
|
||||
#define QRF_ALIGN_NE 7 /* right top */
|
||||
#define QRF_ALIGN_Middle 8 /* auto middle */
|
||||
#define QRF_ALIGN_W 9 /* left middle */
|
||||
#define QRF_ALIGN_C 10 /* center middle */
|
||||
#define QRF_ALIGN_E 11 /* right middle */
|
||||
#define QRF_ALIGN_Bottom 12 /* auto bottom */
|
||||
#define QRF_ALIGN_SW 13 /* left bottom */
|
||||
#define QRF_ALIGN_S 14 /* center bottom */
|
||||
#define QRF_ALIGN_SE 15 /* right bottom */
|
||||
~~~
|
||||
|
||||
Notice how alignment values with an unspecified horizontal
|
||||
or vertical component can be added to another alignment value
|
||||
for which that component is specified, to get a fully
|
||||
specified alignment. For eample:
|
||||
|
||||
> QRF_ALIGN_Center + QRF_ALIGN_Bottom == QRF_ALIGN_S.
|
||||
|
||||
The alignment for column names is always determined by the
|
||||
eTitleAlign setting. If eTitleAlign is QRF_Auto, then column
|
||||
names use center-bottom alignment, QRF_ALIGN_W, value 14.
|
||||
The aAlign\[\] and eDfltAlign settings have no affect on
|
||||
column names.
|
||||
|
||||
For data in the first nAlign columns, the aAlign\[\] array
|
||||
entry for that column takes precedence. If either the horizontal
|
||||
or vertical alignment has an "auto" value for that column or if
|
||||
a column is beyond the first nAlign entries, then eDfltAlign
|
||||
is used as a backup. If neither aAlign\[\] nor eDfltAlign
|
||||
specify a horizontal alignment, then values are left-aligned
|
||||
(QRF_ALIGN_Left). If neither aAlign\[\] nor eDfltAlign
|
||||
specify a vertical alignment, then values are top-aligned
|
||||
(QRF_ALIGN_Top).
|
||||
|
||||
*As of 2025-11-08, only horizontal alignment is implemented.
|
||||
The vertical alignment settings are currently ignored and
|
||||
the vertical alignment is always QRF_ALIGN_Top.*
|
||||
|
||||
### 2.13 Row and Column Separator Strings
|
||||
|
||||
The sqlite3_qrf_spec.zColumnSep and sqlite3_qrf_spec.zRowSep strings
|
||||
are alternative column and row separator character sequences. If not
|
||||
specified (if these pointers are left as NULL) then appropriate defaults
|
||||
are used. Some output styles have hard-coded column and row separators
|
||||
and these settings are ignored for those styles.
|
||||
|
||||
### 2.14 The Output Table Name
|
||||
|
||||
The sqlite3_qrf_spec.zTableName value is the name of the output table
|
||||
when eStyle is QRF_STYLE_Insert.
|
||||
|
||||
### 2.15 The Rendering Of NULL (zNull, eTextNull)
|
||||
|
||||
If a value is NULL then show the NULL using the string
|
||||
found in sqlite3_qrf_spec.zNull. If zNull is itself a NULL pointer
|
||||
then NULL values are rendered as an empty string.
|
||||
|
||||
If the sqlite3_qrf_spec.bTextNull field is QRF_Yes, then the
|
||||
text encoding specified by eText is applied to the value in
|
||||
zNull. If bTextNull is QRF_No or QRF_Auto, then the value
|
||||
in zNull is shown verbatim.
|
||||
|
||||
### 2.16 Optional Value Rendering Callback
|
||||
|
||||
If the sqlite3_qrf_spec.xRender field is not NULL, then each
|
||||
sqlite3_value coming out of the query is first passed to the
|
||||
xRender function, giving that function an opportunity to render
|
||||
the results itself, using whatever custom format is desired.
|
||||
If xRender chooses to render, it should write the rendering
|
||||
into memory obtained from sqlite3_malloc() and return a pointer
|
||||
to that memory. The xRender function can decline
|
||||
to render (for example, based on the sqlite3_value_type() or other
|
||||
characteristics of the value) in which case it can simply return a
|
||||
NULL pointer and the usual default rendering will be used instead.
|
||||
|
||||
The sqlite3_format_query_result() function (which calls xRender)
|
||||
will take responsibility for freeing the string returned by xRender
|
||||
after it has finished using it.
|
||||
|
||||
The eText, eBlob, and eEsc settings above become no-ops if the xRender
|
||||
routine returns non-NULL. In other words, the application-supplied
|
||||
xRender routine is expected to do all of its own quoting and formatting.
|
||||
|
||||
## 3.0 The `sqlite3_format_query_result()` Interface
|
||||
|
||||
Invoke the `sqlite3_format_query_result(P,S,E)` interface to run
|
||||
the prepared statement P and format its results according to the
|
||||
specification found in S. The sqlite3_format_query_result() function
|
||||
will return an SQLite result code, usually SQLITE_OK, but perhaps
|
||||
SQLITE_NOMEM or SQLITE_ERROR or similar. If an error occurs and if
|
||||
the E parameter is not NULL, then error message text might be written
|
||||
into *E. Any error message text will be stored in memory obtained
|
||||
from sqlite3_malloc() and it is the responsibility of the caller to
|
||||
free that memory by a subsequent call to sqlite3_free().
|
||||
|
||||
## 4.0 Output Styles
|
||||
|
||||
The result formatter supports a variety of output styles. The
|
||||
output style used is determined by the eStyle setting of the
|
||||
sqlite3_qrf_spec object. The set of supported output modes
|
||||
might increase in future versions.
|
||||
The following output modes are currently defined:
|
||||
|
||||
> ~~~
|
||||
#define QRF_STYLE_Auto 0 /* Choose a style automatically */
|
||||
#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */
|
||||
#define QRF_STYLE_Column 2 /* One record per line in neat columns */
|
||||
#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */
|
||||
#define QRF_STYLE_Csv 4 /* Comma-separated-value */
|
||||
#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */
|
||||
#define QRF_STYLE_Explain 6 /* EXPLAIN output */
|
||||
#define QRF_STYLE_Html 7 /* Generate an XHTML table */
|
||||
#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */
|
||||
#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */
|
||||
#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */
|
||||
#define QRF_STYLE_Line 11 /* One column per line. */
|
||||
#define QRF_STYLE_List 12 /* One record per line with a separator */
|
||||
#define QRF_STYLE_Markdown 13 /* Markdown formatting */
|
||||
#define QRF_STYLE_Off 14 /* No query output shown */
|
||||
#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */
|
||||
#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */
|
||||
#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */
|
||||
#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */
|
||||
#define QRF_STYLE_Table 19 /* MySQL-style table formatting */
|
||||
~~~
|
||||
|
||||
In the following subsections, these styles will often be referred
|
||||
to without the "QRF_STYLE_" prefix.
|
||||
|
||||
### 4.1 Default Style (Auto)
|
||||
|
||||
The **Auto** style means QRF gets to choose an appropriate output
|
||||
style. It will usually choose **Box**, but might also pick one of
|
||||
**Explain** or **Eqp** if the `sqlite3_stmt_explain()` function
|
||||
returns 1 or 2, respectively.
|
||||
|
||||
### 4.2 Columnar Styles (Box, Column, Markdown, Table)
|
||||
|
||||
The **Box**, **Column**, **Markdown**, and **Table**
|
||||
modes are columnar. This means the output is arranged into neat,
|
||||
uniform-width columns. These styles can use more memory, especially when
|
||||
the query result has many rows, because they need to load the entire output
|
||||
into memory first in order to determine how wide to make each column.
|
||||
|
||||
The nWidth, aWidth, and mxWidth fields of the `sqlite3_qrf_spec` object
|
||||
are used by these styles only, and are ignored by all other styles.
|
||||
The zRowSep and zColumnSep settings are ignored by these styles. The
|
||||
bTitles setting is honored by these styles; it defaults to QRF_SW_On.
|
||||
|
||||
The **Box** style uses Unicode box-drawing character to draw a grid
|
||||
of columns and rows to show the result. The **Table** is the same,
|
||||
except that it uses ASCII-art rather than Unicode box-drawing characters
|
||||
to draw the grid. The **Column** arranges the results in neat columns
|
||||
but does not draw in column or row separator, except that it does draw
|
||||
lines horizontal lines using "`-`" characters to separate the column names
|
||||
from the data below. This is very similar to default output styling in
|
||||
psql. The **Markdown** renders its result in the
|
||||
Markdown table format.
|
||||
|
||||
### 4.3 Line-oriented Styles
|
||||
|
||||
The line-oriented styles output each row of result as it is received from
|
||||
the prepared statement.
|
||||
|
||||
The **List** style is the most familiar line-oriented output format.
|
||||
The **List** style shows output columns for each row on the
|
||||
same line, each separated by a single "`|`" character and with lines
|
||||
terminated by a single newline (\\u000a or \\n). These column
|
||||
and row separator choices can be overridden using the zColumnSep
|
||||
and zRowSep fields of the `sqlite3_qrf_spec` structure. The text
|
||||
formatting is QRF_TEXT_Plain, and BLOB encoding is QRF_BLOB_Text. So
|
||||
characters appear in the output exactly as they appear in the database.
|
||||
Except the eEsp mode defaults to `QRF_ESC_On`, so that control
|
||||
characters are escaped, for safety.
|
||||
|
||||
The **Csv** and **Quote** styles are simply variations on **List**
|
||||
with hard-coded values for some of the sqlite3_qrf_spec settings:
|
||||
|
||||
<table border=1 cellpadding=2 cellspacing=0>
|
||||
<tr><th> <th>Quote<th>Csv
|
||||
<tr><td>zColumnSep<td>","<td>","
|
||||
<tr><td>zRowSep<td>"\\n"<td>"\\r\\n"
|
||||
<tr><td>zNull<td>"NULL"<td>""
|
||||
<tr><td>eText<td>QRF_TEXT_Sql<td>QRF_TEXT_Csv
|
||||
<tr><td>eBlob<td>QRF_BLOB_Sql<td>QRF_BLOB_Text
|
||||
</table>
|
||||
|
||||
The **Html** style generates HTML table content, just without
|
||||
the `<TABLE>..</TABLE>` around the outside.
|
||||
|
||||
The **Insert** style generates a series of SQL "INSERT" statements
|
||||
that will inserts the data that is output into a table whose name is defined
|
||||
by the zTableName field of `sqlite3_qrf_spec`. If zTableName is NULL,
|
||||
then a substitute name is used.
|
||||
|
||||
The **Json** and **JObject** styles generates JSON text for the query result.
|
||||
The **Json** style produces a JSON array of structures with one
|
||||
structure per row. **JObject** outputs independent JSON objects, one per
|
||||
row, with each structure on a separate line all by itself, and not
|
||||
part of a larger array. In both cases, the labels on the elements of the
|
||||
JSON objects are taken from the column names of the SQL query. So if
|
||||
you have an SQL query that has two or more output columns with the same
|
||||
name, you will end up with JSON structures that have duplicate elements.
|
||||
|
||||
Finally, the **Line** style paints each column of a row on a
|
||||
separate line with the column name on the left and a "`=`" separating the
|
||||
column name from its value. A single blank line appears between rows.
|
||||
|
||||
### 4.4 EXPLAIN Styles (Eqp, Explain)
|
||||
|
||||
The **Eqp** and **Explain** styles format output for
|
||||
EXPLAIN QUERY PLAN and EXPLAIN statements, respectively. If the input
|
||||
statement is not already an EXPLAIN QUERY PLAN or EXPLAIN statement is
|
||||
is temporarily converted for the duration of the rendering, but
|
||||
is converted back before `sqlite3_format_query_result()` returns.
|
||||
|
||||
### 4.5 ScanStatus Styles (Stats, StatsEst, StatsVm)
|
||||
|
||||
The **Stats**, **StatsEst**, and **StatsVm** styles are similar to **Eqp**
|
||||
and **Explain** except that they include profiling information
|
||||
from prior executions of the input prepared statement.
|
||||
These modes only work if SQLite has been compiled with
|
||||
-DSQLITE_ENABLE_STMT_SCANSTATUS and if the SQLITE_DBCONFIG_STMT_SCANSTATUS
|
||||
is enabled for the database connection. The **StatsVm** style
|
||||
also requires the bytecode() virtual table which is enabled using
|
||||
the -DSQLITE_ENABLE_BYTECODE_VTAB compile-time option.
|
||||
|
||||
### 4.6 Other Styles (Count, Off)
|
||||
|
||||
The **Count** style discards all query results and returns
|
||||
a count of the number of rows of output at the end. The **Off**
|
||||
style is completely silent; it generates no output. These corner-case
|
||||
modes are sometimes useful for debugging.
|
||||
|
||||
### 5.0 Source Code Files
|
||||
|
||||
The SQLite Query Result Formatter is implemented in three source code files:
|
||||
|
||||
* `qrf.c` → The implementation, written in portable C99
|
||||
* `qrf.h` → A header file defining interfaces
|
||||
* `README.md` → This documentation, in Markdown
|
||||
|
||||
To use the SQLite result formatter, include the "`qrf.h`" header file
|
||||
and link the application against the "`qrf.c`" source file.
|
||||
+2664
File diff suppressed because it is too large
Load Diff
+189
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
** 2025-10-20
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** Header file for the Result-Format or "resfmt" utility library for SQLite.
|
||||
** See the resfmt.md documentation for additional information.
|
||||
*/
|
||||
#ifndef SQLITE_QRF_H
|
||||
#define SQLITE_QRF_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** Specification used by clients to define the output format they want
|
||||
*/
|
||||
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
|
||||
struct sqlite3_qrf_spec {
|
||||
unsigned char iVersion; /* Version number of this structure */
|
||||
unsigned char eStyle; /* Formatting style. "box", "csv", etc... */
|
||||
unsigned char eEsc; /* How to escape control characters in text */
|
||||
unsigned char eText; /* Quoting style for text */
|
||||
unsigned char eTitle; /* Quating style for the text of column names */
|
||||
unsigned char eBlob; /* Quoting style for BLOBs */
|
||||
unsigned char bTitles; /* True to show column names */
|
||||
unsigned char bWordWrap; /* Try to wrap on word boundaries */
|
||||
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
|
||||
unsigned char bTextNull; /* Apply eText encoding to zNull[] */
|
||||
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
|
||||
unsigned char eTitleAlign; /* Alignment for column headers */
|
||||
unsigned char bSplitColumn; /* Wrap single-column output into many columns */
|
||||
short int nWrap; /* Wrap columns wider than this */
|
||||
short int nScreenWidth; /* Maximum overall table width */
|
||||
short int nLineLimit; /* Maximum number of lines for any row */
|
||||
int nCharLimit; /* Maximum number of characters in a cell */
|
||||
int nWidth; /* Number of entries in aWidth[] */
|
||||
int nAlign; /* Number of entries in aAlignment[] */
|
||||
short int *aWidth; /* Column widths */
|
||||
unsigned char *aAlign; /* Column alignments */
|
||||
char *zColumnSep; /* Alternative column separator */
|
||||
char *zRowSep; /* Alternative row separator */
|
||||
char *zTableName; /* Output table name */
|
||||
char *zNull; /* Rendering of NULL */
|
||||
char *(*xRender)(void*,sqlite3_value*); /* Render a value */
|
||||
int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */
|
||||
void *pRenderArg; /* First argument to the xRender callback */
|
||||
void *pWriteArg; /* First argument to the xWrite callback */
|
||||
char **pzOutput; /* Storage location for output string */
|
||||
/* Additional fields may be added in the future */
|
||||
};
|
||||
|
||||
/*
|
||||
** Interfaces
|
||||
*/
|
||||
int sqlite3_format_query_result(
|
||||
sqlite3_stmt *pStmt, /* SQL statement to run */
|
||||
const sqlite3_qrf_spec *pSpec, /* Result format specification */
|
||||
char **pzErr /* OUT: Write error message here */
|
||||
);
|
||||
|
||||
/*
|
||||
** Range of values for sqlite3_qrf_spec.aWidth[] entries and for
|
||||
** sqlite3_qrf_spec.mxColWidth and .nScreenWidth
|
||||
*/
|
||||
#define QRF_MAX_WIDTH 10000
|
||||
#define QRF_MIN_WIDTH 0
|
||||
|
||||
/*
|
||||
** Output styles:
|
||||
*/
|
||||
#define QRF_STYLE_Auto 0 /* Choose a style automatically */
|
||||
#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */
|
||||
#define QRF_STYLE_Column 2 /* One record per line in neat columns */
|
||||
#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */
|
||||
#define QRF_STYLE_Csv 4 /* Comma-separated-value */
|
||||
#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */
|
||||
#define QRF_STYLE_Explain 6 /* EXPLAIN output */
|
||||
#define QRF_STYLE_Html 7 /* Generate an XHTML table */
|
||||
#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */
|
||||
#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */
|
||||
#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */
|
||||
#define QRF_STYLE_Line 11 /* One column per line. */
|
||||
#define QRF_STYLE_List 12 /* One record per line with a separator */
|
||||
#define QRF_STYLE_Markdown 13 /* Markdown formatting */
|
||||
#define QRF_STYLE_Off 14 /* No query output shown */
|
||||
#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */
|
||||
#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */
|
||||
#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */
|
||||
#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */
|
||||
#define QRF_STYLE_Table 19 /* MySQL-style table formatting */
|
||||
|
||||
/*
|
||||
** Quoting styles for text.
|
||||
** Allowed values for sqlite3_qrf_spec.eText
|
||||
*/
|
||||
#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */
|
||||
#define QRF_TEXT_Plain 1 /* Literal text */
|
||||
#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */
|
||||
#define QRF_TEXT_Csv 3 /* CSV-style quoting */
|
||||
#define QRF_TEXT_Html 4 /* HTML-style quoting */
|
||||
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
|
||||
#define QRF_TEXT_Json 6 /* JSON quoting */
|
||||
|
||||
/*
|
||||
** Quoting styles for BLOBs
|
||||
** Allowed values for sqlite3_qrf_spec.eBlob
|
||||
*/
|
||||
#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */
|
||||
#define QRF_BLOB_Text 1 /* Display content exactly as it is */
|
||||
#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */
|
||||
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
|
||||
#define QRF_BLOB_Tcl 4 /* "\000" notation */
|
||||
#define QRF_BLOB_Json 5 /* A JSON string */
|
||||
|
||||
/*
|
||||
** Control-character escape modes.
|
||||
** Allowed values for sqlite3_qrf_spec.eEsc
|
||||
*/
|
||||
#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */
|
||||
#define QRF_ESC_Off 1 /* Do not escape control characters */
|
||||
#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */
|
||||
#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */
|
||||
|
||||
/*
|
||||
** Allowed values for "boolean" fields, such as "bColumnNames", "bWordWrap",
|
||||
** and "bTextJsonb". There is an extra "auto" variants so these are actually
|
||||
** tri-state settings, not booleans.
|
||||
*/
|
||||
#define QRF_SW_Auto 0 /* Let QRF choose the best value */
|
||||
#define QRF_SW_Off 1 /* This setting is forced off */
|
||||
#define QRF_SW_On 2 /* This setting is forced on */
|
||||
#define QRF_Auto 0 /* Alternate spelling for QRF_*_Auto */
|
||||
#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */
|
||||
#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */
|
||||
|
||||
/*
|
||||
** Possible alignment values alignment settings
|
||||
**
|
||||
** Horizontal Vertial
|
||||
** ---------- -------- */
|
||||
#define QRF_ALIGN_Auto 0 /* auto auto */
|
||||
#define QRF_ALIGN_Left 1 /* left auto */
|
||||
#define QRF_ALIGN_Center 2 /* center auto */
|
||||
#define QRF_ALIGN_Right 3 /* right auto */
|
||||
#define QRF_ALIGN_Top 4 /* auto top */
|
||||
#define QRF_ALIGN_NW 5 /* left top */
|
||||
#define QRF_ALIGN_N 6 /* center top */
|
||||
#define QRF_ALIGN_NE 7 /* right top */
|
||||
#define QRF_ALIGN_Middle 8 /* auto middle */
|
||||
#define QRF_ALIGN_W 9 /* left middle */
|
||||
#define QRF_ALIGN_C 10 /* center middle */
|
||||
#define QRF_ALIGN_E 11 /* right middle */
|
||||
#define QRF_ALIGN_Bottom 12 /* auto bottom */
|
||||
#define QRF_ALIGN_SW 13 /* left bottom */
|
||||
#define QRF_ALIGN_S 14 /* center bottom */
|
||||
#define QRF_ALIGN_SE 15 /* right bottom */
|
||||
#define QRF_ALIGN_HMASK 3 /* Horizontal alignment mask */
|
||||
#define QRF_ALIGN_VMASK 12 /* Vertical alignment mask */
|
||||
|
||||
/*
|
||||
** Auxiliary routines contined within this module that might be useful
|
||||
** in other contexts, and which are therefore exported.
|
||||
*/
|
||||
/*
|
||||
** Return an estimate of the width, in columns, for the single Unicode
|
||||
** character c. For normal characters, the answer is always 1. But the
|
||||
** estimate might be 0 or 2 for zero-width and double-width characters.
|
||||
**
|
||||
** Different display devices display unicode using different widths. So
|
||||
** it is impossible to know that true display width with 100% accuracy.
|
||||
** Inaccuracies in the width estimates might cause columns to be misaligned.
|
||||
** Unfortunately, there is nothing we can do about that.
|
||||
*/
|
||||
int sqlite3_qrf_wcwidth(int c);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !defined(SQLITE_QRF_H) */
|
||||
@@ -576,6 +576,10 @@ b.c-pp.target.flags ?=
|
||||
ifeq (1,$(SQLITE_C_IS_SEE))
|
||||
b.c-pp.target.flags += -Denable-see
|
||||
endif
|
||||
api.oo1 ?= 1
|
||||
ifeq (0,$(api.oo1))
|
||||
b.c-pp.target.flags += -Domit-oo1
|
||||
endif
|
||||
|
||||
#
|
||||
# cflags.common = C compiler flags for all builds
|
||||
@@ -906,6 +910,7 @@ sqlite3-api.jses += $(dir.api)/sqlite3-vfs-helper.c-pp.js
|
||||
ifeq (0,$(wasm-bare-bones))
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vtab-helper.c-pp.js
|
||||
endif
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-kvvfs.c-pp.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js
|
||||
|
||||
@@ -993,7 +998,8 @@ endif
|
||||
########################################################################
|
||||
|
||||
EXPORTED_FUNCTIONS.fiddle = $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle
|
||||
$(EXPORTED_FUNCTIONS.fiddle): $(EXPORTED_FUNCTIONS.api.in) $(MAKEFILE_LIST)
|
||||
$(EXPORTED_FUNCTIONS.fiddle): $(EXPORTED_FUNCTIONS.api.in) \
|
||||
$(MAKEFILE_LIST) $(bin.c-pp)
|
||||
@$(call b.mkdir@)
|
||||
@$(call b.c-pp.shcmd,filter,$(EXPORTED_FUNCTIONS.api.in),\
|
||||
$@,$(EXPORTED_FUNCTIONS.c-pp.flags) -Dfiddle)
|
||||
|
||||
@@ -75,6 +75,7 @@ _sqlite3_limit
|
||||
_sqlite3_malloc
|
||||
_sqlite3_malloc64
|
||||
_sqlite3_msize
|
||||
_sqlite3_next_stmt
|
||||
_sqlite3_open
|
||||
_sqlite3_open_v2
|
||||
_sqlite3_overload_function
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
*/
|
||||
ctor._name2vfs = Object.create(null);
|
||||
const isWorkerThread = ('function'===typeof importScripts/*===running in worker thread*/)
|
||||
? (n)=>toss3("The VFS for",n,"is only available in the main window thread.")
|
||||
? (n)=>toss3("VFS",n,"is only available in the main window thread.")
|
||||
: false;
|
||||
ctor._name2vfs[':localStorage:'] = {
|
||||
vfs: 'kvvfs', filename: isWorkerThread || (()=>'local')
|
||||
@@ -2295,55 +2295,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
Stmt
|
||||
}/*oo1 object*/;
|
||||
|
||||
if(util.isUIThread()){
|
||||
/**
|
||||
Functionally equivalent to DB(storageName,'c','kvvfs') except
|
||||
that it throws if the given storage name is not one of 'local'
|
||||
or 'session'.
|
||||
|
||||
As of version 3.46, the argument may optionally be an options
|
||||
object in the form:
|
||||
|
||||
{
|
||||
filename: 'session'|'local',
|
||||
... etc. (all options supported by the DB ctor)
|
||||
}
|
||||
|
||||
noting that the 'vfs' option supported by main DB
|
||||
constructor is ignored here: the vfs is always 'kvvfs'.
|
||||
*/
|
||||
sqlite3.oo1.JsStorageDb = function(storageName='session'){
|
||||
const opt = dbCtorHelper.normalizeArgs(...arguments);
|
||||
storageName = opt.filename;
|
||||
if('session'!==storageName && 'local'!==storageName){
|
||||
toss3("JsStorageDb db name must be one of 'session' or 'local'.");
|
||||
}
|
||||
opt.vfs = 'kvvfs';
|
||||
dbCtorHelper.call(this, opt);
|
||||
};
|
||||
const jdb = sqlite3.oo1.JsStorageDb;
|
||||
jdb.prototype = Object.create(DB.prototype);
|
||||
/** Equivalent to sqlite3_js_kvvfs_clear(). */
|
||||
jdb.clearStorage = capi.sqlite3_js_kvvfs_clear;
|
||||
/**
|
||||
Clears this database instance's storage or throws if this
|
||||
instance has been closed. Returns the number of
|
||||
database blocks which were cleaned up.
|
||||
*/
|
||||
jdb.prototype.clearStorage = function(){
|
||||
return jdb.clearStorage(affirmDbOpen(this).filename);
|
||||
};
|
||||
/** Equivalent to sqlite3_js_kvvfs_size(). */
|
||||
jdb.storageSize = capi.sqlite3_js_kvvfs_size;
|
||||
/**
|
||||
Returns the _approximate_ number of bytes this database takes
|
||||
up in its storage or throws if this instance has been closed.
|
||||
*/
|
||||
jdb.prototype.storageSize = function(){
|
||||
return jdb.storageSize(affirmDbOpen(this).filename);
|
||||
};
|
||||
}/*main-window-only bits*/
|
||||
|
||||
});
|
||||
//#else
|
||||
/* Built with the omit-oo1 flag. */
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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
|
||||
@@ -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){
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,27 +16,35 @@
|
||||
|
||||
More specifically:
|
||||
|
||||
https://fossil.wanderinghorse.net/r/jaccwabyt/file/common/whwasmutil.js
|
||||
https://fossil.wanderinghorse.net/r/jaccwabyt/dir/wasmutil
|
||||
|
||||
and SQLite:
|
||||
|
||||
https://sqlite.org
|
||||
|
||||
This file is kept in sync between both of those trees.
|
||||
|
||||
This build was generated using:
|
||||
|
||||
./c-pp -o js/whwasmutil.js -@policy=error wasmutil/whwasmutil.c-pp.js
|
||||
|
||||
by libcmpp 2.x 2fc4afc31f6505c27b9c34988973a2bd9b157d559247cdd26868ae75632c3a5e @ 2025-11-16 23:03:27.352 UTC
|
||||
*/
|
||||
/**
|
||||
The primary goal of this function is to replace, where possible,
|
||||
Emscripten-generated glue code with equivalent utility code which
|
||||
can be used in arbitrary WASM environments built with toolchains
|
||||
other than Emscripten. To that end, it populates the given object
|
||||
with various WASM-specific APIs. These APIs work with both 32- and
|
||||
64-bit WASM builds.
|
||||
The primary goal of this function is to provide JS/WASM utility
|
||||
code similar to some of that provided by Emscripten-generated
|
||||
builds, the difference being that this one can be used in arbitrary
|
||||
WASM environments built with toolchains other than Emscripten. To
|
||||
that end, it populates the given object with various WASM-specific
|
||||
APIs. These APIs work with both 32- and 64-bit WASM builds.
|
||||
|
||||
Forewarning: this API explicitly targets only browser environments.
|
||||
If a given non-browser environment has the capabilities needed for
|
||||
a given feature (e.g. TextEncoder), great, but it does not go out
|
||||
of its way to account for them and does not provide compatibility
|
||||
crutches for them.
|
||||
crutches for them. That said: no specific incompatibilities with,
|
||||
e.g., node.js are known (whereas it is known that some folks
|
||||
use this with node.js).
|
||||
|
||||
Intended usage:
|
||||
|
||||
@@ -217,7 +225,9 @@
|
||||
newly-created (or config-provided) target. The current approach
|
||||
seemed better at the time.
|
||||
*/
|
||||
globalThis.WhWasmUtilInstaller = function(target){
|
||||
'use strict';
|
||||
globalThis.WhWasmUtilInstaller =
|
||||
function WhWasmUtilInstaller(target){
|
||||
'use strict';
|
||||
if(undefined===target.bigIntEnabled){
|
||||
target.bigIntEnabled = !!globalThis['BigInt64Array'];
|
||||
@@ -766,6 +776,7 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
*/
|
||||
target.uninstallFunction = function(ptr){
|
||||
if(!ptr && __NullPtr!==ptr) return undefined;
|
||||
|
||||
const ft = target.functionTable();
|
||||
cache.freeFuncIndexes.push(ptr);
|
||||
const rc = ft.get(ptr);
|
||||
@@ -2461,7 +2472,8 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
- If `wasmUtilTarget.alloc` is not set and
|
||||
`instance.exports.malloc` is, it installs
|
||||
`wasmUtilTarget.alloc()` and `wasmUtilTarget.dealloc()`
|
||||
wrappers for the exports `malloc` and `free` functions.
|
||||
wrappers for the exports' `malloc` and `free` functions
|
||||
if exports.malloc exists.
|
||||
|
||||
It returns a function which, when called, initiates loading of the
|
||||
module and returns a Promise. When that Promise resolves, it calls
|
||||
@@ -2484,7 +2496,9 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
Error handling is up to the caller, who may attach a `catch()` call
|
||||
to the promise.
|
||||
*/
|
||||
globalThis.WhWasmUtilInstaller.yawl = function(config){
|
||||
globalThis.WhWasmUtilInstaller
|
||||
.yawl = function yawl(config){
|
||||
'use strict';
|
||||
const wfetch = ()=>fetch(config.uri, {credentials: 'same-origin'});
|
||||
const wui = this;
|
||||
const finalThen = function(arg){
|
||||
@@ -2509,7 +2523,7 @@ globalThis.WhWasmUtilInstaller.yawl = function(config){
|
||||
tgt.alloc = function(n){
|
||||
return exports.malloc(n) || toss("Allocation of",n,"bytes failed.");
|
||||
};
|
||||
tgt.dealloc = function(m){exports.free(m)};
|
||||
tgt.dealloc = function(m){m && exports.free(m)};
|
||||
}
|
||||
wui(tgt);
|
||||
}
|
||||
@@ -2528,4 +2542,6 @@ globalThis.WhWasmUtilInstaller.yawl = function(config){
|
||||
.then(finalThen)
|
||||
;
|
||||
return loadWasm;
|
||||
}.bind(globalThis.WhWasmUtilInstaller)/*yawl()*/;
|
||||
}.bind(
|
||||
globalThis.WhWasmUtilInstaller
|
||||
)/*yawl()*/;
|
||||
|
||||
+346
-154
@@ -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);
|
||||
}
|
||||
|
||||
+578
-344
File diff suppressed because it is too large
Load Diff
@@ -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*/)
|
||||
|
||||
@@ -652,7 +652,7 @@ SRC = \
|
||||
$(TOP)/src/sqliteInt.h \
|
||||
$(TOP)/src/sqliteLimit.h \
|
||||
$(TOP)/src/table.c \
|
||||
$(TOP)/src/tclsqlite.c \
|
||||
tclsqlite-ex.c \
|
||||
$(TOP)/src/threads.c \
|
||||
$(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/treeview.c \
|
||||
@@ -1409,15 +1409,15 @@ whereexpr.o: $(TOP)/src/whereexpr.c $(DEPS_OBJ_COMMON)
|
||||
window.o: $(TOP)/src/window.c $(DEPS_OBJ_COMMON)
|
||||
$(T.cc.sqlite) -c $(TOP)/src/window.c
|
||||
|
||||
tclsqlite.o: $(T.tcl.env.sh) $(TOP)/src/tclsqlite.c $(DEPS_OBJ_COMMON)
|
||||
tclsqlite.o: $(T.tcl.env.sh) tclsqlite-ex.c $(DEPS_OBJ_COMMON)
|
||||
$(T.compile.tcl) -DUSE_TCL_STUBS=1 $$TCL_INCLUDE_SPEC \
|
||||
-c $(TOP)/src/tclsqlite.c
|
||||
-c tclsqlite-ex.c -o tclsqlite.o
|
||||
|
||||
tclsqlite-shell.o: $(T.tcl.env.sh) $(TOP)/src/tclsqlite.c $(DEPS_OBJ_COMMON)
|
||||
$(T.compile.tcl) -DTCLSH -o $@ -c $(TOP)/src/tclsqlite.c $$TCL_INCLUDE_SPEC
|
||||
tclsqlite-shell.o: $(T.tcl.env.sh) tclsqlite-ex.c $(DEPS_OBJ_COMMON)
|
||||
$(T.compile.tcl) -DTCLSH -o $@ -c tclsqlite-ex.c $$TCL_INCLUDE_SPEC
|
||||
|
||||
tclsqlite-stubs.o: $(T.tcl.env.sh) $(TOP)/src/tclsqlite.c $(DEPS_OBJ_COMMON)
|
||||
$(T.compile.tcl) -DUSE_TCL_STUBS=1 -o $@ -c $(TOP)/src/tclsqlite.c $$TCL_INCLUDE_SPEC
|
||||
tclsqlite-stubs.o: $(T.tcl.env.sh) tclsqlite-ex.c $(DEPS_OBJ_COMMON)
|
||||
$(T.compile.tcl) -DUSE_TCL_STUBS=1 -o $@ -c tclsqlite-ex.c $$TCL_INCLUDE_SPEC
|
||||
|
||||
#
|
||||
# STATIC_TCLSQLITE3 = 1 to statically link tclsqlite3, else
|
||||
@@ -1661,11 +1661,19 @@ install-tcl-0 install-tcl-:
|
||||
install-tcl: install-tcl-$(HAVE_TCL)
|
||||
install: install-tcl
|
||||
|
||||
tclsqlite3.c: sqlite3.c
|
||||
TCLSQLITEEX = \
|
||||
$(TOP)/ext/qrf/qrf.h \
|
||||
$(TOP)/ext/qrf/qrf.c \
|
||||
$(TOP)/src/tclsqlite.c
|
||||
|
||||
tclsqlite-ex.c: $(TCLSQLITEEX) $(TOP)/tool/mkcombo.tcl $(B.tclsh)
|
||||
$(B.tclsh) $(TOP)/tool/mkcombo.tcl $(TCLSQLITEEX) -o $@
|
||||
|
||||
tclsqlite3.c: sqlite3.c tclsqlite-ex.c
|
||||
echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c
|
||||
cat sqlite3.c >>tclsqlite3.c
|
||||
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
|
||||
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
|
||||
cat tclsqlite-ex.c >>tclsqlite3.c
|
||||
|
||||
#
|
||||
# $(CFLAGS.tclextension) = CFLAGS for the tclextension* targets.
|
||||
@@ -1782,7 +1790,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_STRICT_SUBTYPE=1
|
||||
|
||||
TESTFIXTURE_SRC0 = $(TESTSRC2) $(libsqlite3.LIB)
|
||||
TESTFIXTURE_SRC1 = sqlite3.c
|
||||
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c
|
||||
TESTFIXTURE_SRC = $(TESTSRC) tclsqlite-ex.c
|
||||
TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION))
|
||||
|
||||
testfixture$(T.exe): $(T.tcl.env.sh) has_tclsh85 $(TESTFIXTURE_SRC)
|
||||
@@ -1912,7 +1920,7 @@ shelltest:
|
||||
#
|
||||
sqlite3_analyzer.c.flags.0 = -DINCLUDE_SQLITE3_C=1
|
||||
sqlite3_analyzer.c.flags.1 =
|
||||
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl \
|
||||
sqlite3_analyzer.c: sqlite3.c tclsqlite-ex.c $(TOP)/tool/spaceanal.tcl \
|
||||
$(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in
|
||||
$(B.tclsh) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in \
|
||||
$(sqlite3_analyzer.c.flags.$(LINK_TOOLS_DYNAMICALLY)) \
|
||||
@@ -1936,7 +1944,7 @@ sqlite3_analyzer$(T.exe): $(T.tcl.env.sh) sqlite3_analyzer.c \
|
||||
# can cause the $@ to link to an out-of-tree libsqlite3.so, which may
|
||||
# or may not fail or otherwise cause confusion.
|
||||
|
||||
sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl \
|
||||
sqltclsh.c: sqlite3.c tclsqlite-ex.c $(TOP)/tool/sqltclsh.tcl \
|
||||
$(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl \
|
||||
$(TOP)/tool/sqltclsh.c.in
|
||||
$(B.tclsh) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in >sqltclsh.c
|
||||
@@ -1957,7 +1965,7 @@ xbin: sqlite3_expert$(T.exe)
|
||||
CHECKER_DEPS =\
|
||||
$(TOP)/tool/mkccode.tcl \
|
||||
sqlite3.c \
|
||||
$(TOP)/src/tclsqlite.c \
|
||||
tclsqlite-ex.c \
|
||||
$(TOP)/ext/repair/sqlite3_checker.tcl \
|
||||
$(TOP)/ext/repair/checkindex.c \
|
||||
$(TOP)/ext/repair/checkfreelist.c \
|
||||
@@ -2333,6 +2341,8 @@ mptest: mptester$(T.exe)
|
||||
# Source and header files that shell.c depends on
|
||||
SHELL_DEP = \
|
||||
$(TOP)/src/shell.c.in \
|
||||
$(TOP)/ext/qrf/qrf.c \
|
||||
$(TOP)/ext/qrf/qrf.h \
|
||||
$(TOP)/ext/expert/sqlite3expert.c \
|
||||
$(TOP)/ext/expert/sqlite3expert.h \
|
||||
$(TOP)/ext/intck/sqlite3intck.c \
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
C Experimental\sSQLITE_MAX_SUBQUERY\scompile-time\soption.
|
||||
D 2025-11-18T00:16:40.468
|
||||
C In\sthe\sCLI\son\sthe\s".mode"\scommand,\sreplace\sthe\srecently\sadded\s--no-limits\soption\swith\n"--limits\sL,C".\s\sAnd\sadd\s--sw\sas\san\salias\sfor\s--screenwidth.
|
||||
D 2025-11-24T12:27:18.181
|
||||
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md 6bc480fc673fb4acbc4094e77edb326267dd460162d7723c7f30bee2d3d9e97d
|
||||
F Makefile.in 3ce07126d7e87c7464301482e161fdae6a51d0a2aa06b200b8f0000ef4d6163b
|
||||
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
|
||||
F Makefile.msc 523bab2f6569e912a0aaf2d13e0b3f9bcb36d52310b236d950b354068c9de3f3
|
||||
F Makefile.msc 8c5ed7173ee4cc3c03ba8e21065128f7ca5292c83d68f3bf971786e21fc6e382
|
||||
F README.md dae499194b75deed76a13a4a83c82493f2530331882d7dfe5754d63287d3f8f7
|
||||
F VERSION 74672bfd4c7826c0fc6f84762488a707c52e7d2d94af42ccb0edcc6c74311c41
|
||||
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
|
||||
@@ -18,7 +18,7 @@ F art/sqlite370.svg 40b7e2fe8aac3add5d56dd86ab8d427a4eca5bcb3fe4f8946cb3794e1821
|
||||
F auto.def 44a0d1bf09d78355fc88251ccbf8e64e6341fd89c11de68a01c3645e53a2bade
|
||||
F autoconf/Makefile.fallback 22fe523eb36dfce31e0f6349f782eb084e86a5620b2b0b4f84a2d6133f53f5ac
|
||||
F autoconf/Makefile.in 306f500fce8cae9e1af9459f1ac7fe89ce46999594ef3fdc62dc9d7d651fb091
|
||||
F autoconf/Makefile.msc 803aa9e23b6a764751f6a91d0673620eefa46729fb80faf51391120e1d752966
|
||||
F autoconf/Makefile.msc 7952ed1fbb824f9421688e6b46d09fc4d5a13decec791a6264be4f65e64dfc08
|
||||
F autoconf/README.first f1d3876e9a7852c22f275a6f06814e64934cecbc0b5b9617d64849094c1fd136
|
||||
F autoconf/README.txt b749816b8452b3af994dc6d607394bef3df1736d7e09359f1087de8439a52807
|
||||
F autoconf/auto.def 3d994f3a9cc9b712dbce92a5708570ddcf3b988141b6eb738f2ed16127a9f0ac
|
||||
@@ -47,7 +47,7 @@ F autosetup/find_tclconfig.tcl e64886ffe3b982d4df42cd28ed91fe0b5940c2c5785e126c1
|
||||
F autosetup/jimsh0.c a57c16e65dcffc9c76e496757cb3f7fb47e01ecbd1631a0a5e01751fc856f049
|
||||
F autosetup/pkg-config.tcl 4e635bf39022ff65e0d5434339dd41503ea48fc53822c9c5bde88b02d3d952ba
|
||||
F autosetup/proj.tcl 6fc14ef82b19b77a95788ffbcfad7989b4e3cb4ce96a21dcb5cf7312f362fba9
|
||||
F autosetup/sqlite-config.tcl 1fd7c55394051fbfba2fb9fa9192c027ee7d74612dbf772b06e27feb9f1de5df
|
||||
F autosetup/sqlite-config.tcl 97dc76d332ae3344d02d15c99c57db0ac063537e9133cbee0520d48ba0eddc08
|
||||
F autosetup/system.tcl 51d4be76cd9a9074704b584e5c9cbba616202c8468cf9ba8a4f8294a7ab1dba9
|
||||
F autosetup/teaish/README.txt b40071e6f8506500a2f7f71d5fc69e0bf87b9d7678dd9da1e5b4d0acbf40b1ca
|
||||
F autosetup/teaish/core.tcl e014dd95900c7f9a34e8e0f460f47e94841059827bce8b4c49668b0c7ae3f1a0
|
||||
@@ -60,7 +60,7 @@ F doc/compile-for-unix.md c9dce1ddd4bf0d25efccc5c63eb047e78c01ce06a6ff29c73e0a8a
|
||||
F doc/compile-for-windows.md f9e74d74da88f384edd5809f825035e071608f00f7f39c0e448df7b3982f979c
|
||||
F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f
|
||||
F doc/jsonb.md acd77fc3a709f51242655ad7803510c886aa8304202fa9cf2abc5f5c4e9d7ae5
|
||||
F doc/lemon.html 89ea833a6f71773ab1a9063fbb7fb9b32147bc0b1057b53ecab94a3b30c0aef5
|
||||
F doc/lemon.html 2085fda0a90a94fe92159a79dccc5c30d5a2313524887a31659cd66162f17233
|
||||
F doc/pager-invariants.txt 83aa3a4724b2d7970cc3f3461f0295c46d4fc19a835a5781cbb35cb52feb0577
|
||||
F doc/tcl-extension-testing.md b88861804fc1eaf83249f8e206334189b61e150c360e1b80d0dcf91af82354f5
|
||||
F doc/testrunner.md 5ee928637e03f136a25fef852c5ed975932e31927bd9b05a574424ae18c31019
|
||||
@@ -71,7 +71,7 @@ F doc/wal-lock.md 7db0cd61e2000b545b78ce89b0c2a9a8dd8d64c097839258ac10d7c5c4156e
|
||||
F ext/README.md 6eb1ac267d917767952ed0ef63f55de003b6a5da433ce1fa389e1a9532e73132
|
||||
F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3
|
||||
F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4
|
||||
F ext/expert/expert1.test 1d2da6606623b57bb47064e02140823ce1daecd4cacbf402c73ad3473d7f000c
|
||||
F ext/expert/expert1.test d9dfbf7fb527cfd43049e30a6238ef02c94484041fa4461ed41acbc6435425d6
|
||||
F ext/expert/sqlite3expert.c 546010043fbec93544f762de5161b3d553165859e6bd853c4b85c05f93484260
|
||||
F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b
|
||||
F ext/expert/test_expert.c c395134bd6d4efa594a7d26578a1cb624c4027b79b4b5fcd44736c5ef1f5f725
|
||||
@@ -397,8 +397,8 @@ F ext/misc/shathree.c fd22d70620f86a0467acfdd3acd8435d5cb54eb1e2d9ff36ae44e38982
|
||||
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
|
||||
F ext/misc/spellfix.c 693c8fd3293087fa821322967a97e59dfa24051e5d2ca7fa85790a4034db6fa4
|
||||
F ext/misc/sqlar.c a6175790482328171da47095f87608b48a476d4fac78d8a9ff18b03a2454f634
|
||||
F ext/misc/sqlite3_stdio.c 0fe5a45bd332b30aef2b68c64edbe69e31e9c42365b0fa79ce95a034bca6fbb0
|
||||
F ext/misc/sqlite3_stdio.h f05eaf5e0258f0573910324a789a9586fc360a57678c57a6d63cfaa2245b6176
|
||||
F ext/misc/sqlite3_stdio.c e49c07050bf7bdc87866da7583beda236f2f8c462018a34b61785d99cbddedfd
|
||||
F ext/misc/sqlite3_stdio.h 27a4ecea47e61bc9574ccdf2806f468afe23af2f95028c9b689bfa08ab1ce99f
|
||||
F ext/misc/stmt.c b090086cd6bd6281c21271d38d576eeffe662f0e6b67536352ce32bbaa438321
|
||||
F ext/misc/stmtrand.c 59cffa5d8e158943ff1ce078956d8e208e8c04e67307e8f249dece2436dcb7fc
|
||||
F ext/misc/templatevtab.c 10f15b165b95423ddef593bc5dcb915ec4eb5e0f1066d585e5435a368b8bc22b
|
||||
@@ -416,6 +416,9 @@ F ext/misc/wholenumber.c 0fa0c082676b7868bf2fa918e911133f2b349bcdceabd1198bba5f6
|
||||
F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6292e60c
|
||||
F ext/misc/zipfile.c 09e6e3a3ff40a99677de3c0bc6569bd5f4709b1844ac3d1c1452a456c5a62f1c
|
||||
F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee
|
||||
F ext/qrf/README.md dd565fd1ca0c46ea37dbf4d496e368b9ecade768c92669640bc106e039629016
|
||||
F ext/qrf/qrf.c 7f8498231be49e9fa6290f75c611ccb8658e2d50f0e0dc4207923e59bc63c6f9
|
||||
F ext/qrf/qrf.h bccc9d1c1b6a05fa0da56332bba29d85782318926f022c849114a63da916393f
|
||||
F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8
|
||||
F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255
|
||||
F ext/rbu/rbu10.test 7c22caa32c2ff26983ca8320779a31495a6555737684af7aba3daaf762ef3363
|
||||
@@ -575,7 +578,7 @@ F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009e
|
||||
F ext/session/sqlite3session.c b3de195ce668cace9b324599bf6255a70290cbfb5451e826e946f3aee6e64c54
|
||||
F ext/session/sqlite3session.h 7404723606074fcb2afdc6b72c206072cdb2b7d8ba097ca1559174a80bc26f7a
|
||||
F ext/session/test_session.c 8766b5973a6323934cb51248f621c3dc87ad2a98f023c3cc280d79e7d78d36fb
|
||||
F ext/wasm/GNUmakefile aef238930e96197fed6fa1b4d9e5443b519dd6c24e0139e124c26bc51270f39a
|
||||
F ext/wasm/GNUmakefile 58b851cb13559027499f25050a8c4a103b4c674d97f2ce4e75f2acc8cb112de7
|
||||
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
|
||||
F ext/wasm/README.md 2e87804e12c98f1d194b7a06162a88441d33bb443efcfe00dc6565a780d2f259
|
||||
F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
|
||||
@@ -583,31 +586,32 @@ F ext/wasm/SQLTester/SQLTester.mjs 6b3c52ed36a5573ca4883176f326332a8d4c0cecf5efd
|
||||
F ext/wasm/SQLTester/SQLTester.run.mjs 57f2adb33f43f2784abbf8026c1bfd049d8013af1998e7dcb8b50c89ffc332e0
|
||||
F ext/wasm/SQLTester/index.html 64f3435084c7d6139b08d1f2a713828a73f68de2ae6a3112cbb5980d991ba06f
|
||||
F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.c-pp d609c22bc05e25991bdd2eeed4497485a3756446158b2794471426e5e3517056
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.c-pp 7ba933e8f1290cc65459dd371c0c9a031d96bdf14d7a2244fa761d9775117b90
|
||||
F ext/wasm/api/README.md aae128e14711dafac29e7b645ff1b6be42305b73f74b1303d8401351f8448631
|
||||
F ext/wasm/api/extern-post-js.c-pp.js 4b310c9891886e7c1ea4e2419966ff45dfeda825b897fdaa4119332509f7e3d1
|
||||
F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41
|
||||
F ext/wasm/api/post-js-footer.js a50c1a2c4d008aede7b2aa1f18891a7ee71437c2f415b8aeb3db237ddce2935b
|
||||
F ext/wasm/api/post-js-header.js d24bd0d065f3489c8b78ddf3ead6321e5d047187a162cd503c41700e03dd1f06
|
||||
F ext/wasm/api/pre-js.c-pp.js ad2546290e0c8ce5ca2081bff6e85cc25eeb904a3303921f1184290a7ff1b32f
|
||||
F ext/wasm/api/sqlite3-api-glue.c-pp.js 79a54b54ca6324d28e31e19b56bbaebb7d2cc4b3079066e7e901333fa5047c53
|
||||
F ext/wasm/api/sqlite3-api-oo1.c-pp.js 31dbfd470c91ffd96d77399b749bab6b69e3ba9074188833f97ac13f087cf07b
|
||||
F ext/wasm/api/sqlite3-api-prologue.js a7cab3e8f78d5a93d7173657677fef18265f48d4e0b1e1755740f734a69cae6d
|
||||
F ext/wasm/api/sqlite3-api-glue.c-pp.js 9b33e3ee467791dec4fd1b444b12a8545dfbb6c8b28ac651c7bdc7661a3b5a5c
|
||||
F ext/wasm/api/sqlite3-api-oo1.c-pp.js 8ce38bd4b22aa2b0311c7a8e87e748e06213766fe2141de7574672d103ece255
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 7004b569624765c5132984bfecee2305bef928a6adf44e0202dacc9cbc5c8e2a
|
||||
F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667430f9d45167569633ffc7567938
|
||||
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
|
||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js 9654b565b346dc609b75d15337f20acfa7af7d9d558da1afeb9b6d8eaa404966
|
||||
F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c8fb7f0630264e6c7fa0e57515d
|
||||
F ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js f44a4396b2305b27dc81c00a1629fe4f615520f7fb11ed4e606255e365956ba2
|
||||
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 26cb41d5a62f46a106b6371eb00fef02de3cdbfaa51338ba087a45f53028e0d0
|
||||
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js aa330fa0e8ef35cbd92eb0d52e05fbaa07e61540c5cb164e693c82428ce1d763
|
||||
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 88ce2078267a2d1af57525a32d896295f4a8db7664de0e17e82dc9ff006ed8d3
|
||||
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js 9097074724172e31e56ce20ccd7482259cf72a76124213cbc9469d757676da86
|
||||
F ext/wasm/api/sqlite3-wasm.c 03451f8335f4ba8ea9a79dfc170560b1ee5376de64ff49c1a114274ccf50ab3d
|
||||
F ext/wasm/api/sqlite3-wasm.c 7778527bdbb241b8e742a5fc50e470e8b8d0529c41fab9ac127c2c63f3c90d47
|
||||
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bda1c75bd674a92a0e27cc2f3d46dbbf21e422413f8046814515a0bd7409328a
|
||||
F ext/wasm/api/sqlite3-worker1.c-pp.js 802d69ead8c38dc1be52c83afbfc77e757da8a91a2e159e7ed3ecda8b8dba2e7
|
||||
F ext/wasm/c-pp-lite.c 943be1a36774d58385dca32de36fc18d4f432fe79f7aa35e6c85dd6a6b825bd0
|
||||
F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51
|
||||
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
|
||||
F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f
|
||||
F ext/wasm/common/whwasmutil.js 804409dec98458f732b23e6e86788934ac6dec002107435cc4055355585d1c15
|
||||
F ext/wasm/common/whwasmutil.js 831f07a0d9bb61713164871370811432e96d0f813806a4d2c783d3c77c2373a0
|
||||
F ext/wasm/config.make.in c424ae1cc3c89274520ad312509d36c4daa34a3fce5d0c688e5f8f4365e1049a
|
||||
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
|
||||
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
|
||||
@@ -624,8 +628,8 @@ F ext/wasm/fiddle/fiddle.js 84fd75967e0af8b69d3dd849818342227d0f81d13db92e0dcbc6
|
||||
F ext/wasm/fiddle/index.html a27b8127ef9ecf19612da93b2a6a73bdb3777b5c56b5450bb7200a94bc108ff9
|
||||
F ext/wasm/index-dist.html db23748044e286773f2768eec287669501703b5d5f72755e8db73607dc54d290
|
||||
F ext/wasm/index.html 54e27db740695ab2cb296e02d42c4c66b3f11b65797340d19fa6590f5b287da1
|
||||
F ext/wasm/jaccwabyt/jaccwabyt.js 1e734c624205cdf621f322972dfb0fc8013d573a5882f57492a6830e5ec23e17
|
||||
F ext/wasm/jaccwabyt/jaccwabyt.md 167fc0b624c9bc2c477846e336de9403842d81b1a24fc4d3b24317cb9eba734f
|
||||
F ext/wasm/jaccwabyt/jaccwabyt.js 4e2b797dc170851c9c530c3567679f4aa509eec0fab73b466d945b00b356574b
|
||||
F ext/wasm/jaccwabyt/jaccwabyt.md 6aa90fa1a973d0ad10d077088bea163b241d8470c75eafdef87620a1de1dea41
|
||||
F ext/wasm/mkdist.sh 64d53f469c823ed311f6696f69cec9093f745e467334b34f5ceabdf9de3c5b28 x
|
||||
F ext/wasm/mkwasmbuilds.c ef42e404236dd98cedb6ecea47b6d2474e3c593633ce6d992d316289dfc442b6
|
||||
F ext/wasm/module-symbols.html e54f42112e0aac2a31f850ab33e7f2630a2ea4f63496f484a12469a2501e07e2
|
||||
@@ -643,7 +647,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
|
||||
F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c
|
||||
F ext/wasm/tester1-worker.c-pp.html 0e432ec2c0d99cd470484337066e8d27e7aee4641d97115338f7d962bf7b081a
|
||||
F ext/wasm/tester1.c-pp.html 52d88fe2c6f21a046030a36410b4839b632f4424028197a45a3d5669ea724ddb
|
||||
F ext/wasm/tester1.c-pp.js 2c255093205a0dac9dae7475030665c2c9d6dccc857de68ee7daf49aa82e6de8
|
||||
F ext/wasm/tester1.c-pp.js 015b4133cc3a5fb41d6236a6b39d23d996cc2d61a4877acde31a1f69574d4ce3
|
||||
F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e
|
||||
F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88
|
||||
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
|
||||
@@ -653,7 +657,7 @@ F ext/wasm/tests/opfs/sahpool/index.html be736567fd92d3ecb9754c145755037cbbd2bca
|
||||
F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36e0f6991460fff0cb7c15079454679a4e2
|
||||
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
|
||||
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
|
||||
F main.mk 29078d11f7545a8fae4347305b164bb4e777b801da16baeb48214014a8d63a06
|
||||
F main.mk 27a0769b40a813343f19e8fca2e9d55405f0311cb3cc5327f5702640df0182a0
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
|
||||
@@ -663,7 +667,7 @@ F mptest/multiwrite01.test dab5c5f8f9534971efce679152c5146da265222d
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 1b9c24374a85dfc7eb8fa7c4266ee0db4f9609cceecfc5481cd8307e5af04366
|
||||
F sqlite3.pc.in e6dee284fba59ef500092fdc1843df3be8433323a3733c91da96690a50a5b398
|
||||
F src/alter.c fc7bbbeb9e89c7124bf5772ce474b333b7bdc18d6e080763211a40fde69fb1da
|
||||
F src/alter.c f31437552c733957f19351cdfae8fad8e8f0c7d11041e5b7966aae57206ad91f
|
||||
F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d
|
||||
F src/attach.c 9af61b63b10ee702b1594ecd24fb8cea0839cfdb6addee52fba26fa879f5db9d
|
||||
F src/auth.c 54ab9c6c5803b47c0d45b76ce27eff22a03b4b1f767c5945a3a4eb13aa4c78dc
|
||||
@@ -673,7 +677,7 @@ F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea
|
||||
F src/btree.c 8850125300b9780fa54bc45a41af88eb2796b90f2f97942279094beef9b0e971
|
||||
F src/btree.h e823c46d87f63d904d735a24b76146d19f51f04445ea561f71cc3382fd1307f0
|
||||
F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886
|
||||
F src/build.c 62cf7d885a93d87dcdb2a314408b760367cd9d9b54f8d4ea7d71d0cd36f0ab4c
|
||||
F src/build.c 611e07299d72ff04bbcb9e7109183467e30925d203c3e121ef9bb3cf6876289b
|
||||
F src/callback.c 3605bbf02bd7ed46c79cd48346db4a32fc51d67624400539c0532f4eead804ad
|
||||
F src/carray.c ff6081a31878fc34df8fa1052a9cbf17ddc22652544dcb3e2326886ed1053b55
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
@@ -688,13 +692,13 @@ F src/func.c 0b802107498048d3dcac0b757720bcb8506507ce02159e213ab8161458eb293b
|
||||
F src/global.c a19e4b1ca1335f560e9560e590fc13081e21f670643367f99cb9e8f9dc7d615b
|
||||
F src/hash.c 03c8c0f4be9e8bcb6de65aa26d34a61d48a9430747084a69f9469fbb00ea52ca
|
||||
F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf
|
||||
F src/hwtime.h d4d1b98c1afdc56986a5c0eba882786ef017e2180a320da25b4231b8e7e463e8
|
||||
F src/hwtime.h 21c2cf1f736e7b97502c3674d0c386db3f06870d6f10d0cf8174e2a4b8cb726e
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c dfd311b0ac2d4f6359e62013db67799757f4d2cc56cca5c10f4888acfbbfa3fd
|
||||
F src/json.c fb031340edee159c07ad37dbe668ffe945ed86f525b0eb3822e4a67cbc498a72
|
||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||
F src/loadext.c 4747f72084dc80520a2d23e5bb0bc0760672208a81a2b115ce0b657acd65edf4
|
||||
F src/main.c eaf5215917b7e8b34ddea013524287f6d5021ba0fc70202e7602e225ef0d16bf
|
||||
F src/main.c c99e86fd7dc6c8c2c0f67395562e3300bca2780b845db9bdc2bdac6acf13c6aa
|
||||
F src/malloc.c 410e570b30c26cc36e3372577df50f7a96ee3eed5b2b161c6b6b48773c650c5e
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
|
||||
@@ -715,33 +719,33 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
|
||||
F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
|
||||
F src/os_kv.c fb7ba8d6204197357f1eb7e1c7450d09c10043bf7e99aba602f4aa46b8fb11a3
|
||||
F src/os_setup.h 8efc64eda6a6c2f221387eefc2e7e45fd5a3d5c8337a7a83519ba4fbd2957ae2
|
||||
F src/os_unix.c 7945ede1e85b2d1b910e1b4af9ba342e964b1e30e79f4176480a60736445cb36
|
||||
F src/os_unix.c bf55d195730393a9faf31dcec65257710c61f61a728342151af06a8267ffb35d
|
||||
F src/os_win.c a89b501fc195085c7d6c9eec7f5bd782625e94bb2a96b000f4d009703df1083f
|
||||
F src/os_win.h 4c247cdb6d407c75186c94a1e84d5a22cbae4adcec93fcae8d2bc1f956fd1f19
|
||||
F src/pager.c a81461de271ac4886ad75b7ca2cca8157a48635820c4646cd2714acdc2c17e5f
|
||||
F src/pager.c 52bae5ea11f25cbdc425be7b0a8c3310b3ec2a1381e73b5d5b6af20c26094108
|
||||
F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8
|
||||
F src/parse.y 619c3e92a54686c5e47923688c4b9bf7ec534a4690db5677acc28b299c403250
|
||||
F src/parse.y 424d195ea70f4656a3f6440e0b79ca8f712ae4da9431033a19ec8c9816469287
|
||||
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
|
||||
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
|
||||
F src/pcache1.c 131ca0daf4e66b4608d2945ae76d6ed90de3f60539afbd5ef9ec65667a5f2fcd
|
||||
F src/pragma.c ecec75795c1821520266e4f93fa8840cce48979af532db06f085e36a7813860f
|
||||
F src/prepare.c 2af0b5c1ec787c8eebd21baa9d79caf4a4dc3a18e76ce2edbf2027d706bca37a
|
||||
F src/prepare.c f6a6e28a281bd1d1da12f47d370a81af46159b40f73bf7fa0b276b664f9c8b7d
|
||||
F src/printf.c b1b29b5e58e1530d5daeee5963d3c318d8ab2d7e38437580e28755753e0c1ded
|
||||
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||
F src/resolve.c 5616fbcf3b833c7c705b24371828215ad0925d0c0073216c4f153348d5753f0a
|
||||
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
|
||||
F src/select.c ba9cd07ffa3277883c1986085f6ddc4320f4d35d5f212ab58df79a7ecc1a576a
|
||||
F src/shell.c.in ceb0a9cc008ac82d8d2e6ef353db14a54bc40dfd60a8cfbb6bc98d071f538761
|
||||
F src/sqlite.h.in 667dff873941366da98da3200cf757ac05dfb62ea785e6642e33dd586ae5285c
|
||||
F src/shell.c.in bc2185e0e988f93643dbaac3c8f190b3687475cd7ac802c035d20347b8e27fbb
|
||||
F src/sqlite.h.in b5b2450724c9aa2d7ae2861c881385bc21ba940c79122c893f5ae9bb36ad10dd
|
||||
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
|
||||
F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998
|
||||
F src/sqliteInt.h 775c133757bd5c7d0fce10407959ee45993678ae95088e4ebdb22ef24e9d78ff
|
||||
F src/sqliteLimit.h fe70bd8983e5d317a264f2ea97473b359faf3ebb0827877a76813f5cf0cdc364
|
||||
F src/sqliteInt.h a89c3a9296928dffcb4c287df176a739f9cf620c7c9d33aec59e8efb9b39cbbd
|
||||
F src/sqliteLimit.h 0a5516b4ec192a205c541e05f67009028a9451dc6678aae4cf8e68596903c246
|
||||
F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
F src/tclsqlite.c 3c604c49e6cf4211960a9ddb9505280fd22cde32175f40884c641c0f5a286036
|
||||
F src/tclsqlite.c f5aca78b32b2ba823a6dc7dbb9b2272c93e0f71df2f93104d5a61a6348f3b115
|
||||
F src/tclsqlite.h 614b3780a62522bc9f8f2b9fb22689e8009958e7aa77e572d0f3149050af348a
|
||||
F src/test1.c 5d061afe479c7364842e0170be7220dea13389575fa6030d30b3e20bec4e1f75
|
||||
F src/test1.c 0e71fbcb484a271564e98e0158192c28c24f5521594218c3ba48bcb4cf634f91
|
||||
F src/test2.c 62f0830958f9075692c29c6de51b495ae8969e1bef85f239ffcd9ba5fb44a5ff
|
||||
F src/test3.c 432646f581d8af1bb495e58fc98234380250954f5d5535e507fc785eccc3987a
|
||||
F src/test4.c 0ac87fc13cdb334ab3a71823f99b6c32a6bebe5d603cd6a71d84c823d43a25a0
|
||||
@@ -771,7 +775,7 @@ F src/test_md5.c 811a45330c9391933360f998156a8907ee29909c828ab83ac05d329942cbea8
|
||||
F src/test_multiplex.c 82f0aa8eee629b6949782cfab8782ed35a9b56dc80d12877af52147f304d22b8
|
||||
F src/test_multiplex.h f0ff5b6f4462bfd46dac165d6375b9530d08089b7bcbe75e88e0926110db5363
|
||||
F src/test_mutex.c dacae6790956c0d4e705aaed2090227792e291b0496cccd688e9994c1e21f740
|
||||
F src/test_onefile.c f31e52e891c5fef6709b9fcef54ce660648a34172423a9cbdf4cbce3ba0049f4
|
||||
F src/test_onefile.c f52ae709489837863521e6e0e1a8965b6716d45f784445b98641b6ce0fc2137b
|
||||
F src/test_osinst.c 269039d9c0820a02ee928014c30860d57ee757ecda54df42e463d0ca1377b835
|
||||
F src/test_pcache.c 496da3f7e2ca66aefbc36bbf22138b1eff43ba0dff175c228b760fa020a37bd0
|
||||
F src/test_quota.c 180e87437250bed7e17e4e61c106730939e39fec9be73d28961f27f579a92078
|
||||
@@ -785,7 +789,7 @@ F src/test_tclsh.c c01706ac60bd3176754d3ccd37da74c6ad97c2e14489f8ed71b497c1c0ac0
|
||||
F src/test_tclvar.c ae873248a0188459b1c16ca7cc431265dacce524399e8b46725c2b3b7e048424
|
||||
F src/test_thread.c 3edb4a5b5aeb1a6e9a275dccc848ac95acab7f496b3e9230f6d2d04953a2b862
|
||||
F src/test_vdbecov.c 5c426d9cd2b351f5f9ceb30cabf8c64a63bfcad644c507e0bd9ce2f6ae1a3bf3
|
||||
F src/test_vfs.c b4135c1308516adf0dfd494e6d6c33114e03732be899eace0502919b674586b5
|
||||
F src/test_vfs.c 7b06f2f82584a9e723e62cb6894d60d21dc4cd9bcd02d6de976e5434e5d60a30
|
||||
F src/test_window.c 6d80e11fba89a1796525e6f0048ff0c7789aa2c6b0b11c80827dc1437bd8ea72
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
@@ -831,12 +835,14 @@ F test/alter2.test 7e3d26ab409df52df887b366a63902c3429b935c41cb962fd58ffc25784f2
|
||||
F test/alter3.test dcdd5f850f30656a45a0f05e41abfb52b74bbf6ccba165d0f7adf6b0116e4fd6
|
||||
F test/alter4.test 37cafe164067a6590a0ee4cec780bddbbaa33dc50b11542dcfbe0e65626494fd
|
||||
F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256bafc959
|
||||
F test/alterauth2.test 48967abae0494d9a300d1c92473d99fcb66edfcc23579c89322f033f49410adc
|
||||
F test/altercol.test b43fb5725332f4cf8cff0280605202c1672e808281accea60a066d2ccc5129e5
|
||||
F test/alterauth2.test 4b74fa8f184f4736497317feb587b65759eb87d87acfe3a8ef433d4d18bb002b
|
||||
F test/altercol.test 3661c432aacb42bc2198dd4611bbb9c3b09fc73251b59edda046109103b8ac00
|
||||
F test/altercons.test ea18def4a0f26b9066da56095c9c480df705df4d02e4ae151708fae76f7e3884
|
||||
F test/altercons2.test 3c1f58312817df43aeada3b1827fdc3ce3fc50c6f49a95ef62cf4cbbae8583a0
|
||||
F test/altercorrupt.test 2e1d705342cf9d7de884518ddbb053fd52d7e60d2b8869b7b63b2fda68435c12
|
||||
F test/alterdropcol.test a653a3945f964d26845ec0cd0a8e74189f46de3119a984c5bc45457da392612e
|
||||
F test/alterdropcol2.test 527fce683b200d620f560f666c44ae33e22728e990a10a48a543280dfd4b4d41
|
||||
F test/alterfault.test 289067108947bedca27534edd4ff251bcd298cf84402d7b24eaa3749305418c6
|
||||
F test/alterfault.test 9baff95be91ad77e955701454fa9e4bdd8c94f7fe6e814939962d3d5efb83220
|
||||
F test/alterlegacy.test f38c6d06cda39e1f7b955bbce57f2e3ef5b7cb566d3d1234502093e228c15811
|
||||
F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9
|
||||
F test/altermalloc2.test 17fb3724c4b004c469c27dc4ef181608aa644555fbd3f3236767584f73747c81
|
||||
@@ -883,7 +889,7 @@ F test/autoindex5.test 3fb938cbf4e7f3896563ce04e2a24b0bc653fc6245b4bf3268cd7b20f
|
||||
F test/autovacuum.test 00671369bbf96c6a49989a9425f5b78b94075d6a4b031e5e00000c2c32f365df
|
||||
F test/autovacuum2.test 76f7eb4fe6a6bf6d33a196a7141dba98886d2fb53a268d7feca285d5da4759d7
|
||||
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
|
||||
F test/avfs.test 76f59743dc1f5fa533840d1818b420fe1ee45e21c0fd6bbac7942ba677903128
|
||||
F test/avfs.test 95bb8d04f8edad6dc9e600221d103f7e2cc3da398af84df215a3a819e560c45c
|
||||
F test/avtrans.test 7a6eae44763293024b137b53ff824d8500d754dbae060a8d940afbacfc1d4a15
|
||||
F test/backcompat.test f2431465ed668f09fc3f6998e56e893a1506ccea6e8b6f409f085f759f431b48
|
||||
F test/backup.test 3b08fd4af69f0fa786931103a31f4542b184aba16e239e5f22b18c3c2476697f
|
||||
@@ -1420,7 +1426,7 @@ F test/misc1.test e3e36262aff1bd9b8b9bf1eeb3af04adb3fc1e23f0a92dbff708bba9e939ac
|
||||
F test/misc2.test a1a3573cc02662becd967766021d6f16c54684d56df5f227481c7ef0d9df0bd0
|
||||
F test/misc3.test 651b88bca19b8ff6a7b6af73dae00c3fd5b3ea5bee0c0d1d91abd4c4b4748718
|
||||
F test/misc4.test 10cd6addb2fa9093df4751a1b92b50440175dd5468a6ec84d0386e78f087db0e
|
||||
F test/misc5.test 02fcaf4d42405be02ec975e946270a50b0282dac98c78303ade0d1392839d2b8
|
||||
F test/misc5.test 0a5d7604e197f10ee471280bfcaaf8229f9d8e2eebfef2c8853222cbc1ea9cd5
|
||||
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
|
||||
F test/misc7.test d595599972ec0b436985f0f02f243b68500ffc977b9b3194ec66c0866cfddcab
|
||||
F test/misc8.test 08d2380bc435486b12161521f225043ac2be26f02471c2c1ea4cac0b1548edbd
|
||||
@@ -1433,6 +1439,7 @@ F test/mmap4.test 2e2b4e32555b58da15176e6fe750f17c9dcf7f93
|
||||
F test/mmapcorrupt.test 470fb44fe92e99c1d23701d156f8c17865f5b027063c9119dcfdb842791f4465
|
||||
F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3
|
||||
F test/mmapwarm.test 2272005969cd17a910077bd5082f70bc1fefad9a875afec7fc9af483898ecaf3
|
||||
F test/modeA.clitest 43098858465029ebe9229009fbd59528adf89f265fb82af87fc5c58f7901b2bc
|
||||
F test/multiplex.test d74c034e52805f6de8cc5432cef8c9eb774bb64ec29b83a22effc8ca4dac1f08
|
||||
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
|
||||
F test/multiplex3.test fac575e0b1b852025575a6a8357701d80933e98b5d2fe6d35ddaa68f92f6a1f7
|
||||
@@ -1502,6 +1509,10 @@ F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224c
|
||||
F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb
|
||||
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
|
||||
F test/pushdown.test 46a626ef1c0ca79b85296ff2e078b9da20a50e9b804b38f441590c3987580ddd
|
||||
F test/qrf01.test a6ecc051613ea4dde796e4ff20fdb2c59a2138faf926d90a2a92c1e291776617
|
||||
F test/qrf02.test 39b4afdc000bedccdafc0aecf17638df67a67aaa2d2942865ae6abcc48ba0e92
|
||||
F test/qrf03.test 9de53aea459f5a127283db03cbb6011500757685646d21aa3c29c44c6ef23e86
|
||||
F test/qrf04.test 0894692c998d2401dcc33449c02051b503ecce0c94217be54fb007c82d2d1379
|
||||
F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
|
||||
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
|
||||
F test/quickcheck.test a4b7e878cd97e46108291c409b0bf8214f29e18fddd68a42bc5c1375ad1fb80a
|
||||
@@ -1514,7 +1525,7 @@ F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
|
||||
F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736
|
||||
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
|
||||
F test/readonly.test 0d307c335b3421898cfe64a783a376138aa003849b6bff61ee2d21e805bc0051
|
||||
F test/recover.test c76d05f33f0271fba0f0752170e03b0ab5952dc61dcea7ab3ba40df03c4c42de
|
||||
F test/recover.test 643139b911ac880a1e881d7621f02cfb546b608b8f2494d7d26fd5ed103b1ceb
|
||||
F test/regexp1.test 8f2a8bc1569666e29a4cee6c1a666cd224eb6d50e2470d1dc1df995170f3e0f1
|
||||
F test/regexp2.test 64f9726b2ddc71aea06725fcad53231833d038d58b936d49083ace658b370a13
|
||||
F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d
|
||||
@@ -1595,16 +1606,17 @@ F test/sharedA.test 64bdd21216dda2c6a3bd3475348ccdc108160f34682c97f2f51c19fc0e21
|
||||
F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8ee707
|
||||
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
|
||||
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
|
||||
F test/shell1.test ebe953d64c937ad42a0f33170ac0d2d2568faae26813fc7a95203756446d54aa
|
||||
F test/shell2.test ab23f01ea2347e4b72bb2399af7ee82aa00f9c059141749f7c4064abca5ad728
|
||||
F test/shell3.test 603b448e917537cf77be0f265c05c6f63bc677c63a533c8e96aae923b56f4a0e
|
||||
F test/shell4.test 03593fa7908a55f255916ffeda707cdf55680c777736e3da62b1d78cde0d684d
|
||||
F test/shell5.test d17e7927ab8b7f720efbdd9b5d05fceb6c3c56c25917901b315400214bf24ef4
|
||||
F test/shell1.test 9438957ab1c0d4c4622b98a11648999a86b6184c8317a87d3c5cb760d2f7bca9
|
||||
F test/shell2.test 103140814bdc7508aa41dd3462413cbc4aa84b4261112cb8d501d74275cb7d48
|
||||
F test/shell3.test 840192774cc4edf7653520c0434a311c7477b9bc324abbc7bd2887915792fa8c
|
||||
F test/shell4.test e25580a792b7b54560c3a76b6968bd8189261f38979fe28e6bc6312c5db280db
|
||||
F test/shell5.test 7a249400bb2af59ac8524f357f8cf2844a62b6ac5ff8ecd69b045ceb688700ae
|
||||
F test/shell6.test e3b883b61d4916b6906678a35f9d19054861123ad91b856461e0a456273bdbb8
|
||||
F test/shell7.test 43fd8e511c533bab5232e95c7b4be93b243451709e89582600d4b6e67693d5c3
|
||||
F test/shell8.test 641cf21a99c59404c24e3062923734951c4099a6b6b6520de00cf7a1249ee871
|
||||
F test/shell9.test 8742a5b390cdcef6369f5aa223e415aa4255a4129ef249b177887dc635a87209
|
||||
F test/shellA.test 4ecff8b7b2c0122ba8174abfbcc4b0f59e44d80f2a911068f8cd4cfc6661032d
|
||||
F test/shellA.test 4b8983a40703b5550f691592b6c5bc64886c26755eeb7a474af849d1e39b51f2
|
||||
F test/shellB.test de879b1ea7c25daf1a06b2c882b45a5d002e6580c81c57169ce47084cc6afb6b
|
||||
F test/shmlock.test 9f1f729a7fe2c46c88b156af819ac9b72c0714ac6f7246638a73c5752b5fd13c
|
||||
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
|
||||
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
|
||||
@@ -1678,7 +1690,7 @@ F test/tabfunc01.test 56eeae736217204bb1d9f9ef38340d48058f809b64249217cf77ff4ba6
|
||||
F test/table.test e87294bf1c80bfd7792142b84ab32ea5beb4f3f71e535d7fb263a6b2068377bf
|
||||
F test/tableapi.test e37c33e6be2276e3a96bb54b00eea7f321277115d10e5b30fdb52a112b432750
|
||||
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
|
||||
F test/tclsqlite.test 3f697424cfc1cdc9c076ec0cadb0e700f059400a3e3ce134b7d856fc9f880e1c
|
||||
F test/tclsqlite.test 5d6c73bfe7006c85e2f7fb7db8638b521eb2043d5451aaacdac4851eab895443
|
||||
F test/tempdb.test 4cdaa23ddd8acb4d79cbb1b68ccdfd09b0537aaba909ca69a876157c2a2cbd08
|
||||
F test/tempdb2.test 353864e96fd3ae2f70773d0ffbf8b1fe48589b02c2ec05013b540879410c3440
|
||||
F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900
|
||||
@@ -1690,7 +1702,7 @@ F test/tester.tcl 463ae33b8bf75ac77451df19bd65e7c415c2e9891227c7c9e657d0a2d8e107
|
||||
F test/testloadext.c 862b848783eaed9985fbce46c65cd214664376b549fae252b364d5d1ef350a27
|
||||
F test/testrunner.tcl 86179a8e78997e9257cb8f738c5624cb23897da5297855578ba74715e64f1602 x
|
||||
F test/testrunner_data.tcl c507a9afa911c03446ed90442ffd4a98aca02882c3d51bd1177c24795674def8
|
||||
F test/testrunner_estwork.tcl 7927a84327259a32854926f68a75292e33a61e7e052fdbfcb01f18696c99c724
|
||||
F test/testrunner_estwork.tcl 81e2ae10238f50540f42fbf2d94913052a99bfb494b69e546506323f195dcff9
|
||||
F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
|
||||
F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@@ -1913,7 +1925,7 @@ F test/upsertfault.test f21ca47740841fdb4d61acfa7b17646d773e67724fe8c185b71c018d
|
||||
F test/uri.test 1250724af9beeed2d6c3716f5b990c483200c54f408d3c0ec9543a3c7961f8fc
|
||||
F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
|
||||
F test/utf16align.test 9fde0bb5d3a821594aa68c6829ab9c5453a084384137ebb9f6153e2d678039da
|
||||
F test/vacuum-into.test 5a489714feecfdabfc7b293be4111564a173dee92c0d6818dd0207f3ade65783
|
||||
F test/vacuum-into.test ebd223213fde4c985818b9d6dc33fe29a7cdd4272738d82e3e14aa2e686d228b
|
||||
F test/vacuum.test f3b2257a4fcd659513c866a5d9e5f70999cc58fd5d74e979290385fa350b79ee
|
||||
F test/vacuum2.test 9fd45ce6ce29f5614c249e03938d3567c06a9e772d4f155949f8eafe2d8af520
|
||||
F test/vacuum3.test d9d9a04ee58c485b94694fd4f68cffaba49c32234fdefe1ac1a622c5e17d4ce3
|
||||
@@ -2101,8 +2113,8 @@ F tool/genfkey.README e550911fa984c8255ebed2ef97824125d83806eb5232582700de949edf
|
||||
F tool/genfkey.test b6afd7b825d797a1e1274f519ab5695373552ecad5cd373530c63533638a5a4f
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
F tool/index_usage.c f62a0c701b2c7ff2f3e21d206f093c123f222dbf07136a10ffd1ca15a5c706c5
|
||||
F tool/lemon.c 8f6c122e5727cb0e5f302b8efc91489b1947a8d98206d7a1b1cfc0ed685b6e7c
|
||||
F tool/lempar.c bdffd3b233a4e4e78056c9c01fadd2bb3fe902435abde3bce3d769fdf0d5cca2
|
||||
F tool/lemon.c 4150f2020d453cfa46b6fa45542e59b923ad7eab063fb4ca20777995622cab0b
|
||||
F tool/lempar.c b57e1780bf8098dd4a9a5bba537f994276ea825a420f6165153e5894dc2dfb07
|
||||
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
|
||||
F tool/loadfts.c 63412f9790e5e8538fbde0b4f6db154aaaf80f7a10a01e3c94d14b773a8dd5a6
|
||||
F tool/logest.c c34e5944318415de513d29a6098df247a9618c96d83c38d4abd88641fe46e669
|
||||
@@ -2111,6 +2123,7 @@ F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a19
|
||||
F tool/mkamalzip.tcl 8aa5ebe7973c8b8774062d34e15fea9815c4cc2ceea3a9b184695f005910876a
|
||||
F tool/mkautoconfamal.sh 647dada5e34c466bef62a4408e1c99a7e5e1922805479dd57944f33f9803f2f8
|
||||
F tool/mkccode.tcl c42a8f8cf78f92e83795d5447460dbce7aaf78a3bbf9082f1507dc71a3665f3c x
|
||||
F tool/mkcombo.tcl 2a5189b219c4a495e1ff7fc980bd568d3cfb82ae9d50c84e77f7a161e96fc132
|
||||
F tool/mkctimec.tcl 3fb5cad05922f5da61262cb6bcd5868a34e94a49ca8833ae2d7796e7df075576 x
|
||||
F tool/mkkeywordhash.c 6b0be901c47f9ad42215fc995eb2f4384ac49213b1fba395102ec3e999acf559
|
||||
F tool/mkmsvcmin.tcl d76c45efda1cce2d4005bcea7b8a22bb752e3256009f331120fb4fecb14ebb7a
|
||||
@@ -2118,7 +2131,7 @@ F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61
|
||||
F tool/mkopcodeh.tcl 2b4e6967a670ef21bf53a164964c35c6163277d002a4c6f56fa231d68c88d023
|
||||
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
|
||||
F tool/mkpragmatab.tcl 3801ce32f8c55fe63a3b279f231fb26c2c1a2ea9a09d2dd599239d87a609acec
|
||||
F tool/mkshellc.tcl bab0a72a68384181a5706712dfdf6815f6526446d4e8aacace2de5e80cda91b2
|
||||
F tool/mkshellc.tcl a9a4d421a35b7117bb9d50e8e533c59a67f8a03957e2dd1282b92d27fb87e74c
|
||||
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
|
||||
F tool/mksqlite3c-noext.tcl 351c55256213154cabb051a3c870ef9f4487de905015141ae50dc7578a901b84
|
||||
F tool/mksqlite3c.tcl 7a268139158e5deef27a370bc2f8db6ccf100c1ad7ac5e5b23743c0fd354f609
|
||||
@@ -2149,7 +2162,7 @@ F tool/split-sqlite3c.tcl 4969fd642dad0ea483e4e104163021d92baf98f6a8eac981fe4852
|
||||
F tool/sqldiff.c 134be7866be19f8beb32043d5aea5657f01aaeae2df8d33d758ff722c78666b9
|
||||
F tool/sqlite3_analyzer.c.in 14f02cb5ec3c264cd6107d1f1dad77092b1cf440fc196c30b69ae87b56a1a43b
|
||||
F tool/sqlite3_rsync.c d0e58a1e49fe2192c3ee0b697aed182d502bebfe5b4b406ba6b2baa52a04ecbe
|
||||
F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaaef898
|
||||
F tool/sqltclsh.c.in c103c6fc7d42bce611f9d4596774d60b7ef3d0b291a1f58c9e6184e458b89296
|
||||
F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848
|
||||
F tool/src-verify.c 6c655d9a8d6b30f3648fc78a79bf3838ed68f8543869d380c43ea9f17b3b8501
|
||||
F tool/srcck1.c 559e703c6cca1d70398bdba1d7f91036c1a71adf718a1aaa6401a562ccaed154
|
||||
@@ -2166,11 +2179,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd
|
||||
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
|
||||
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 6621737cc05cbf8ff5f576775a8a3c64f666b56d42939968ebb55d72a835646b
|
||||
R d682117481a7b1cf594f7dd8ef6eff70
|
||||
T *branch * max-subquery
|
||||
T *sym-max-subquery *
|
||||
T -sym-trunk *
|
||||
P ff2065b533769cbf5278d13d09ffa93a7a0f841b1b59b6be0e5f27e3d1c341d0
|
||||
R 628a0162ba8f0d8ab2de4d5bcae00f5f
|
||||
U drh
|
||||
Z 62a7b6290233b3d348ba184dec1ce219
|
||||
Z 990cca7d1ccfde0bfbdaf6f519eba45c
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
+2
-2
@@ -1,2 +1,2 @@
|
||||
branch max-subquery
|
||||
tag max-subquery
|
||||
branch remove-sync-dataonly
|
||||
tag remove-sync-dataonly
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
35ec117e5a20f30030a37d2fbd059654bb13d0ec64fcf8719d71d9a4318dc970
|
||||
caefe4c746599b22aec446f3aeb296e53edaf8c54f8ae803381e8c21d5e29ab4
|
||||
|
||||
+720
-4
@@ -491,7 +491,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
|
||||
/* Look up the table being altered. */
|
||||
assert( pParse->pNewTable==0 );
|
||||
assert( sqlite3BtreeHoldsAllMutexes(db) );
|
||||
if( db->mallocFailed ) goto exit_begin_add_column;
|
||||
if( NEVER(db->mallocFailed) ) goto exit_begin_add_column;
|
||||
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
|
||||
if( !pTab ) goto exit_begin_add_column;
|
||||
|
||||
@@ -563,7 +563,7 @@ exit_begin_add_column:
|
||||
** Or, if pTab is not a view or virtual table, zero is returned.
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
|
||||
static int isRealTable(Parse *pParse, Table *pTab, int bDrop){
|
||||
static int isRealTable(Parse *pParse, Table *pTab, int iOp){
|
||||
const char *zType = 0;
|
||||
#ifndef SQLITE_OMIT_VIEW
|
||||
if( IsView(pTab) ){
|
||||
@@ -576,9 +576,12 @@ static int isRealTable(Parse *pParse, Table *pTab, int bDrop){
|
||||
}
|
||||
#endif
|
||||
if( zType ){
|
||||
const char *azMsg[] = {
|
||||
"rename columns of", "drop column from", "edit constraints of"
|
||||
};
|
||||
assert( iOp>=0 && iOp<ArraySize(azMsg) );
|
||||
sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"",
|
||||
(bDrop ? "drop column from" : "rename columns of"),
|
||||
zType, pTab->zName
|
||||
azMsg[iOp], zType, pTab->zName
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
@@ -1049,6 +1052,25 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
|
||||
return pBest;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the error message of the context passed as the first argument to
|
||||
** the result of formatting zFmt using printf() style formatting.
|
||||
*/
|
||||
static void errorMPrintf(sqlite3_context *pCtx, const char *zFmt, ...){
|
||||
sqlite3 *db = sqlite3_context_db_handle(pCtx);
|
||||
char *zErr = 0;
|
||||
va_list ap;
|
||||
va_start(ap, zFmt);
|
||||
zErr = sqlite3VMPrintf(db, zFmt, ap);
|
||||
va_end(ap);
|
||||
if( zErr ){
|
||||
sqlite3_result_error(pCtx, zErr, -1);
|
||||
sqlite3DbFree(db, zErr);
|
||||
}else{
|
||||
sqlite3_result_error_nomem(pCtx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** An error occurred while parsing or otherwise processing a database
|
||||
** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an
|
||||
@@ -2313,6 +2335,696 @@ exit_drop_column:
|
||||
sqlite3SrcListDelete(db, pSrc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes of leading whitespace/comments in string z[].
|
||||
*/
|
||||
static int getWhitespace(const u8 *z){
|
||||
int nRet = 0;
|
||||
while( 1 ){
|
||||
int t = 0;
|
||||
int n = sqlite3GetToken(&z[nRet], &t);
|
||||
if( t!=TK_SPACE && t!=TK_COMMENT ) break;
|
||||
nRet += n;
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return the number of bytes until the end of the next non-whitespace and
|
||||
** non-comment token. For the purpose of this function, a "(" token includes
|
||||
** all of the bytes through and including the matching ")", or until the
|
||||
** first illegal token, whichever comes first.
|
||||
**
|
||||
** Write the token type into *piToken.
|
||||
**
|
||||
** The value returned is the number of bytes in the token itself plus
|
||||
** the number of bytes of leading whitespace and comments skipped plus
|
||||
** all bytes through the next matching ")" if the token is TK_LP.
|
||||
**
|
||||
** Example: (Note: '.' used in place of '*' in the example z[] text)
|
||||
**
|
||||
** ,--------- *piToken := TK_RP
|
||||
** v
|
||||
** z[] = " /.comment./ --comment\n (two three four) five"
|
||||
** | |
|
||||
** |<-------------------------------------->|
|
||||
** |
|
||||
** `--- return value
|
||||
*/
|
||||
static int getConstraintToken(const u8 *z, int *piToken){
|
||||
int iOff = 0;
|
||||
int t = 0;
|
||||
do {
|
||||
iOff += sqlite3GetToken(&z[iOff], &t);
|
||||
}while( t==TK_SPACE || t==TK_COMMENT );
|
||||
|
||||
*piToken = t;
|
||||
|
||||
if( t==TK_LP ){
|
||||
int nNest = 1;
|
||||
while( nNest>0 ){
|
||||
iOff += sqlite3GetToken(&z[iOff], &t);
|
||||
if( t==TK_LP ){
|
||||
nNest++;
|
||||
}else if( t==TK_RP ){
|
||||
t = TK_LP;
|
||||
nNest--;
|
||||
}else if( t==TK_ILLEGAL ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*piToken = t;
|
||||
return iOff;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument z points into the body of a constraint - specifically the
|
||||
** second token of the constraint definition. For a named constraint,
|
||||
** z points to the first token past the CONSTRAINT keyword. For an
|
||||
** unnamed NOT NULL constraint, z points to the first byte past the NOT
|
||||
** keyword.
|
||||
**
|
||||
** Return the number of bytes until the end of the constraint.
|
||||
*/
|
||||
static int getConstraint(const u8 *z){
|
||||
int iOff = 0;
|
||||
int t = 0;
|
||||
|
||||
/* Now, the current constraint proceeds until the next occurence of one
|
||||
** of the following tokens:
|
||||
**
|
||||
** CONSTRAINT, PRIMARY, NOT, UNIQUE, CHECK, DEFAULT,
|
||||
** COLLATE, REFERENCES, FOREIGN, GENERATED, AS, RP, or COMMA
|
||||
**
|
||||
** Also exit the loop if ILLEGAL turns up.
|
||||
*/
|
||||
while( 1 ){
|
||||
int n = getConstraintToken(&z[iOff], &t);
|
||||
if( t==TK_CONSTRAINT || t==TK_PRIMARY || t==TK_NOT || t==TK_UNIQUE
|
||||
|| t==TK_CHECK || t==TK_DEFAULT || t==TK_COLLATE || t==TK_REFERENCES
|
||||
|| t==TK_FOREIGN || t==TK_RP || t==TK_COMMA || t==TK_ILLEGAL
|
||||
|| t==TK_AS || t==TK_GENERATED
|
||||
){
|
||||
break;
|
||||
}
|
||||
iOff += n;
|
||||
}
|
||||
|
||||
return iOff;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compare two constraint names.
|
||||
**
|
||||
** Summary: *pRes := zQuote != zCmp
|
||||
**
|
||||
** Details:
|
||||
** Compare the (possibly quoted) constraint name zQuote[0..nQuote-1]
|
||||
** against zCmp[]. Write zero into *pRes if they are the same and
|
||||
** non-zero if they differ. Normally return SQLITE_OK, except if there
|
||||
** is an OOM, set the OOM error condition on ctx and return SQLITE_NOMEM.
|
||||
*/
|
||||
static int quotedCompare(
|
||||
sqlite3_context *ctx, /* Function context on which to report errors */
|
||||
const u8 *zQuote, /* Possibly quoted text. Not zero-terminated. */
|
||||
int nQuote, /* Length of zQuote in bytes */
|
||||
const u8 *zCmp, /* Zero-terminated, unquoted name to compare against */
|
||||
int *pRes /* OUT: Set to 0 if equal, non-zero if unequal */
|
||||
){
|
||||
char *zCopy = 0; /* De-quoted, zero-terminated copy of zQuote[] */
|
||||
|
||||
zCopy = sqlite3MallocZero(nQuote+1);
|
||||
if( zCopy==0 ){
|
||||
sqlite3_result_error_nomem(ctx);
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
memcpy(zCopy, zQuote, nQuote);
|
||||
sqlite3Dequote(zCopy);
|
||||
*pRes = sqlite3_stricmp((const char*)zCopy, (const char*)zCmp);
|
||||
sqlite3_free(zCopy);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** zSql[] is a CREATE TABLE statement, supposedly. Find the offset
|
||||
** into zSql[] of the first character past the first "(" and write
|
||||
** that offset into *piOff and return SQLITE_OK. Or, if not found,
|
||||
** set the SQLITE_CORRUPT error code and return SQLITE_ERROR.
|
||||
*/
|
||||
static int skipCreateTable(sqlite3_context *ctx, const u8 *zSql, int *piOff){
|
||||
int iOff = 0;
|
||||
|
||||
if( zSql==0 ) return SQLITE_ERROR;
|
||||
|
||||
/* Jump past the "CREATE TABLE" bit. */
|
||||
while( 1 ){
|
||||
int t = 0;
|
||||
iOff += sqlite3GetToken(&zSql[iOff], &t);
|
||||
if( t==TK_LP ) break;
|
||||
if( t==TK_ILLEGAL ){
|
||||
sqlite3_result_error_code(ctx, SQLITE_CORRUPT_BKPT);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
*piOff = iOff;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Internal SQL function sqlite3_drop_constraint(): Given an input
|
||||
** CREATE TABLE statement, return a revised CREATE TABLE statement
|
||||
** with a constraint removed. Two forms, depending on the datatype
|
||||
** of argv[2]:
|
||||
**
|
||||
** sqlite_drop_constraint(SQL, INT) -- Omit NOT NULL from the INT-th column
|
||||
** sqlite_drop_constraint(SQL, TEXT) -- OMIT constraint with name TEXT
|
||||
**
|
||||
** In the first case, the left-most column is 0.
|
||||
*/
|
||||
static void dropConstraintFunc(
|
||||
sqlite3_context *ctx,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const u8 *zSql = sqlite3_value_text(argv[0]);
|
||||
const u8 *zCons = 0;
|
||||
int iNotNull = -1;
|
||||
int ii;
|
||||
int iOff = 0;
|
||||
int iStart = 0;
|
||||
int iEnd = 0;
|
||||
char *zNew = 0;
|
||||
int t = 0;
|
||||
sqlite3 *db;
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
if( zSql==0 ) return;
|
||||
|
||||
/* Jump past the "CREATE TABLE" bit. */
|
||||
if( skipCreateTable(ctx, zSql, &iOff) ) return;
|
||||
|
||||
if( sqlite3_value_type(argv[1])==SQLITE_INTEGER ){
|
||||
iNotNull = sqlite3_value_int(argv[1]);
|
||||
}else{
|
||||
zCons = sqlite3_value_text(argv[1]);
|
||||
}
|
||||
|
||||
/* Search for the named constraint within column definitions. */
|
||||
for(ii=0; iEnd==0; ii++){
|
||||
|
||||
/* Now parse the column or table constraint definition. Search
|
||||
** for the token CONSTRAINT if this is a DROP CONSTRAINT command, or
|
||||
** NOT in the right column if this is a DROP NOT NULL. */
|
||||
while( 1 ){
|
||||
iStart = iOff;
|
||||
iOff += getConstraintToken(&zSql[iOff], &t);
|
||||
if( t==TK_CONSTRAINT && (zCons || iNotNull==ii) ){
|
||||
/* Check if this is the constraint we are searching for. */
|
||||
int nTok = 0;
|
||||
int cmp = 1;
|
||||
|
||||
/* Skip past any whitespace. */
|
||||
iOff += getWhitespace(&zSql[iOff]);
|
||||
|
||||
/* Compare the next token - which may be quoted - with the name of
|
||||
** the constraint being dropped. */
|
||||
nTok = getConstraintToken(&zSql[iOff], &t);
|
||||
if( zCons ){
|
||||
if( quotedCompare(ctx, &zSql[iOff], nTok, zCons, &cmp) ) return;
|
||||
}
|
||||
iOff += nTok;
|
||||
|
||||
/* The next token is usually the first token of the constraint
|
||||
** definition. This is enough to tell the type of the constraint -
|
||||
** TK_NOT means it is a NOT NULL, TK_CHECK a CHECK constraint etc.
|
||||
**
|
||||
** There is also the chance that the next token is TK_CONSTRAINT
|
||||
** (or TK_DEFAULT or TK_COLLATE), for example if a table has been
|
||||
** created as follows:
|
||||
**
|
||||
** CREATE TABLE t1(cols, CONSTRAINT one CONSTRAINT two NOT NULL);
|
||||
**
|
||||
** In this case, allow the "CONSTRAINT one" bit to be dropped by
|
||||
** this command if that is what is requested, or to advance to
|
||||
** the next iteration of the loop with &zSql[iOff] still pointing
|
||||
** to the CONSTRAINT keyword. */
|
||||
nTok = getConstraintToken(&zSql[iOff], &t);
|
||||
if( t==TK_CONSTRAINT || t==TK_DEFAULT || t==TK_COLLATE
|
||||
|| t==TK_COMMA || t==TK_RP || t==TK_GENERATED || t==TK_AS
|
||||
){
|
||||
t = TK_CHECK;
|
||||
}else{
|
||||
iOff += nTok;
|
||||
iOff += getConstraint(&zSql[iOff]);
|
||||
}
|
||||
|
||||
if( cmp==0 || (iNotNull>=0 && t==TK_NOT) ){
|
||||
if( t!=TK_NOT && t!=TK_CHECK ){
|
||||
errorMPrintf(ctx, "constraint may not be dropped: %s", zCons);
|
||||
return;
|
||||
}
|
||||
iEnd = iOff;
|
||||
break;
|
||||
}
|
||||
|
||||
}else if( t==TK_NOT && iNotNull==ii ){
|
||||
iEnd = iOff + getConstraint(&zSql[iOff]);
|
||||
break;
|
||||
}else if( t==TK_RP || t==TK_ILLEGAL ){
|
||||
iEnd = -1;
|
||||
break;
|
||||
}else if( t==TK_COMMA ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the constraint has not been found it is an error. */
|
||||
if( iEnd<=0 ){
|
||||
if( zCons ){
|
||||
errorMPrintf(ctx, "no such constraint: %s", zCons);
|
||||
}else{
|
||||
/* SQLite follows postgres in that a DROP NOT NULL on a column that is
|
||||
** not NOT NULL is not an error. So just return the original SQL here. */
|
||||
sqlite3_result_text(ctx, (const char*)zSql, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
}else{
|
||||
|
||||
/* Figure out if an extra space should be inserted after the constraint
|
||||
** is removed. And if an additional comma preceding the constraint
|
||||
** should be removed. */
|
||||
const char *zSpace = " ";
|
||||
iEnd += getWhitespace(&zSql[iEnd]);
|
||||
sqlite3GetToken(&zSql[iEnd], &t);
|
||||
if( t==TK_RP || t==TK_COMMA ){
|
||||
zSpace = "";
|
||||
if( zSql[iStart-1]==',' ) iStart--;
|
||||
}
|
||||
|
||||
db = sqlite3_context_db_handle(ctx);
|
||||
zNew = sqlite3MPrintf(db, "%.*s%s%s", iStart, zSql, zSpace, &zSql[iEnd]);
|
||||
sqlite3_result_text(ctx, zNew, -1, SQLITE_DYNAMIC);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Internal SQL function:
|
||||
**
|
||||
** sqlite_add_constraint(SQL, CONSTRAINT-TEXT, ICOL)
|
||||
**
|
||||
** SQL is a CREATE TABLE statement. Return a modified version of
|
||||
** SQL that adds CONSTRAINT-TEXT at the end of the ICOL-th column
|
||||
** definition. (The left-most column defintion is 0.)
|
||||
*/
|
||||
static void addConstraintFunc(
|
||||
sqlite3_context *ctx,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const u8 *zSql = sqlite3_value_text(argv[0]);
|
||||
const char *zCons = (const char*)sqlite3_value_text(argv[1]);
|
||||
int iCol = sqlite3_value_int(argv[2]);
|
||||
int iOff = 0;
|
||||
int ii;
|
||||
char *zNew = 0;
|
||||
int t = 0;
|
||||
sqlite3 *db;
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
if( skipCreateTable(ctx, zSql, &iOff) ) return;
|
||||
|
||||
for(ii=0; ii<=iCol || (iCol<0 && t!=TK_RP); ii++){
|
||||
iOff += getConstraintToken(&zSql[iOff], &t);
|
||||
while( 1 ){
|
||||
int nTok = getConstraintToken(&zSql[iOff], &t);
|
||||
if( t==TK_COMMA || t==TK_RP ) break;
|
||||
if( t==TK_ILLEGAL ){
|
||||
sqlite3_result_error_code(ctx, SQLITE_CORRUPT_BKPT);
|
||||
return;
|
||||
}
|
||||
iOff += nTok;
|
||||
}
|
||||
}
|
||||
|
||||
iOff += getWhitespace(&zSql[iOff]);
|
||||
|
||||
db = sqlite3_context_db_handle(ctx);
|
||||
if( iCol<0 ){
|
||||
zNew = sqlite3MPrintf(db, "%.*s, %s%s", iOff, zSql, zCons, &zSql[iOff]);
|
||||
}else{
|
||||
zNew = sqlite3MPrintf(db, "%.*s %s%s", iOff, zSql, zCons, &zSql[iOff]);
|
||||
}
|
||||
sqlite3_result_text(ctx, zNew, -1, SQLITE_DYNAMIC);
|
||||
}
|
||||
|
||||
/*
|
||||
** Find a column named pCol in table pTab. If successful, set output
|
||||
** parameter *piCol to the index of the column in the table and return
|
||||
** SQLITE_OK. Otherwise, set *piCol to -1 and return an SQLite error
|
||||
** code.
|
||||
*/
|
||||
static int alterFindCol(Parse *pParse, Table *pTab, Token *pCol, int *piCol){
|
||||
sqlite3 *db = pParse->db;
|
||||
char *zName = sqlite3NameFromToken(db, pCol);
|
||||
int rc = SQLITE_NOMEM;
|
||||
int iCol = -1;
|
||||
|
||||
if( zName ){
|
||||
iCol = sqlite3ColumnIndex(pTab, zName);
|
||||
if( iCol<0 ){
|
||||
sqlite3ErrorMsg(pParse, "no such column: %s", zName);
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( rc==SQLITE_OK ){
|
||||
const char *zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
|
||||
const char *zCol = pTab->aCol[iCol].zCnName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){
|
||||
pTab = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3DbFree(db, zName);
|
||||
*piCol = iCol;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Find the table named by the first entry in source list pSrc. If successful,
|
||||
** return a pointer to the Table structure and set output variable (*pzDb)
|
||||
** to point to the name of the database containin the table (i.e. "main",
|
||||
** "temp" or the name of an attached database).
|
||||
**
|
||||
** If the table cannot be located, return NULL. The value of the two output
|
||||
** parameters is undefined in this case.
|
||||
*/
|
||||
static Table *alterFindTable(
|
||||
Parse *pParse, /* Parsing context */
|
||||
SrcList *pSrc, /* Name of the table to look for */
|
||||
int *piDb, /* OUT: write the iDb here */
|
||||
const char **pzDb, /* OUT: write name of schema here */
|
||||
int bAuth /* Do ALTER TABLE authorization checks if true */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
Table *pTab = 0;
|
||||
assert( sqlite3BtreeHoldsAllMutexes(db) );
|
||||
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
|
||||
if( pTab ){
|
||||
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
*pzDb = db->aDb[iDb].zDbSName;
|
||||
*piDb = iDb;
|
||||
|
||||
if( SQLITE_OK!=isRealTable(pParse, pTab, 2)
|
||||
|| SQLITE_OK!=isAlterableTable(pParse, pTab)
|
||||
){
|
||||
pTab = 0;
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( pTab && bAuth ){
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, *pzDb, pTab->zName, 0) ){
|
||||
pTab = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
sqlite3SrcListDelete(db, pSrc);
|
||||
return pTab;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate bytecode for one of:
|
||||
**
|
||||
** (1) ALTER TABLE pSrc DROP CONSTRAINT pCons
|
||||
** (2) ALTER TABLE pSrc ALTER pCol DROP NOT NULL
|
||||
**
|
||||
** One of pCons and pCol must be NULL and the other non-null.
|
||||
*/
|
||||
void sqlite3AlterDropConstraint(
|
||||
Parse *pParse, /* Parsing context */
|
||||
SrcList *pSrc, /* The table being altered */
|
||||
Token *pCons, /* Name of the constraint to drop */
|
||||
Token *pCol /* Name of the column from which to remove the NOT NULL */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
Table *pTab = 0;
|
||||
int iDb = 0;
|
||||
const char *zDb = 0;
|
||||
char *zArg = 0;
|
||||
|
||||
assert( (pCol==0)!=(pCons==0) );
|
||||
assert( pSrc->nSrc==1 );
|
||||
pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, pCons!=0);
|
||||
if( !pTab ) return;
|
||||
|
||||
if( pCons ){
|
||||
zArg = sqlite3MPrintf(db, "%.*Q", pCons->n, pCons->z);
|
||||
}else{
|
||||
int iCol;
|
||||
if( alterFindCol(pParse, pTab, pCol, &iCol) ) return;
|
||||
zArg = sqlite3MPrintf(db, "%d", iCol);
|
||||
}
|
||||
|
||||
/* Edit the SQL for the named table. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
|
||||
"sql = sqlite_drop_constraint(sql, %s) "
|
||||
"WHERE type='table' AND tbl_name=%Q COLLATE nocase"
|
||||
, zDb, zArg, pTab->zName
|
||||
);
|
||||
sqlite3DbFree(db, zArg);
|
||||
|
||||
/* Finally, reload the database schema. */
|
||||
renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons);
|
||||
}
|
||||
|
||||
/*
|
||||
** The implementation of SQL function sqlite_fail(MSG). This takes a single
|
||||
** argument, and returns it as an error message with the error code set to
|
||||
** SQLITE_CONSTRAINT.
|
||||
*/
|
||||
static void failConstraintFunc(
|
||||
sqlite3_context *ctx,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zText = (const char*)sqlite3_value_text(argv[0]);
|
||||
int err = sqlite3_value_int(argv[1]);
|
||||
(void)NotUsed;
|
||||
sqlite3_result_error(ctx, zText, -1);
|
||||
sqlite3_result_error_code(ctx, err);
|
||||
}
|
||||
|
||||
/*
|
||||
** Buffer pCons, which is nCons bytes in size, contains the text of a
|
||||
** NOT NULL or CHECK constraint that will be inserted into a CREATE TABLE
|
||||
** statement. If successful, this function returns the size of the buffer in
|
||||
** bytes not including any trailing whitespace or "--" style comments. Or,
|
||||
** if an OOM occurs, it returns 0 and sets db->mallocFailed to true.
|
||||
**
|
||||
** C-style comments at the end are preserved. "--" style comments are
|
||||
** removed because the comment terminator might be \000, and we are about
|
||||
** to insert the pCons[] text into the middle of a larger string, and that
|
||||
** will have the effect of removing the comment terminator and messing up
|
||||
** the syntax.
|
||||
*/
|
||||
static int alterRtrimConstraint(
|
||||
sqlite3 *db, /* used to record OOM error */
|
||||
const char *pCons, /* Buffer containing constraint */
|
||||
int nCons /* Size of pCons in bytes */
|
||||
){
|
||||
u8 *zTmp = (u8*)sqlite3MPrintf(db, "%.*s", nCons, pCons);
|
||||
int iOff = 0;
|
||||
int iEnd = 0;
|
||||
|
||||
if( zTmp==0 ) return 0;
|
||||
|
||||
while( 1 ){
|
||||
int t = 0;
|
||||
int nToken = sqlite3GetToken(&zTmp[iOff], &t);
|
||||
if( t==TK_ILLEGAL ) break;
|
||||
if( t!=TK_SPACE && (t!=TK_COMMENT || zTmp[iOff]!='-') ){
|
||||
iEnd = iOff+nToken;
|
||||
}
|
||||
iOff += nToken;
|
||||
}
|
||||
|
||||
sqlite3DbFree(db, zTmp);
|
||||
return iEnd;
|
||||
}
|
||||
|
||||
/*
|
||||
** Prepare a statement of the form:
|
||||
**
|
||||
** ALTER TABLE pSrc ALTER pCol SET NOT NULL
|
||||
*/
|
||||
void sqlite3AlterSetNotNull(
|
||||
Parse *pParse, /* Parsing context */
|
||||
SrcList *pSrc, /* Name of the table being altered */
|
||||
Token *pCol, /* Name of the column to add a NOT NULL constraint to */
|
||||
Token *pFirst /* The NOT token of the NOT NULL constraint text */
|
||||
){
|
||||
Table *pTab = 0;
|
||||
int iCol = 0;
|
||||
int iDb = 0;
|
||||
const char *zDb = 0;
|
||||
const char *pCons = 0;
|
||||
int nCons = 0;
|
||||
|
||||
/* Look up the table being altered. */
|
||||
assert( pSrc->nSrc==1 );
|
||||
pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, 0);
|
||||
if( !pTab ) return;
|
||||
|
||||
/* Find the column being altered. */
|
||||
if( alterFindCol(pParse, pTab, pCol, &iCol) ){
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find the length in bytes of the constraint definition */
|
||||
pCons = pFirst->z;
|
||||
nCons = alterRtrimConstraint(pParse->db, pCons, pParse->sLastToken.z - pCons);
|
||||
|
||||
/* Search for a constraint violation. Throw an exception if one is found. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"SELECT sqlite_fail('constraint failed', %d) "
|
||||
"FROM %Q.%Q AS x WHERE x.%.*s IS NULL",
|
||||
SQLITE_CONSTRAINT, zDb, pTab->zName, (int)pCol->n, pCol->z
|
||||
);
|
||||
|
||||
/* Edit the SQL for the named table. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
|
||||
"sql = sqlite_add_constraint(sqlite_drop_constraint(sql, %d), %.*Q, %d) "
|
||||
"WHERE type='table' AND tbl_name=%Q COLLATE nocase"
|
||||
, zDb, iCol, nCons, pCons, iCol, pTab->zName
|
||||
);
|
||||
|
||||
/* Finally, reload the database schema. */
|
||||
renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of internal SQL function:
|
||||
**
|
||||
** sqlite_find_constraint(SQL, CONSTRAINT-NAME)
|
||||
**
|
||||
** This function returns true if the SQL passed as the first argument is a
|
||||
** CREATE TABLE that contains a constraint with the name CONSTRAINT-NAME,
|
||||
** or false otherwise.
|
||||
*/
|
||||
static void findConstraintFunc(
|
||||
sqlite3_context *ctx,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const u8 *zSql = 0;
|
||||
const u8 *zCons = 0;
|
||||
int iOff = 0;
|
||||
int t = 0;
|
||||
|
||||
(void)NotUsed;
|
||||
zSql = sqlite3_value_text(argv[0]);
|
||||
zCons = sqlite3_value_text(argv[1]);
|
||||
|
||||
if( zSql==0 || zCons==0 ) return;
|
||||
while( t!=TK_LP && t!=TK_ILLEGAL ){
|
||||
iOff += sqlite3GetToken(&zSql[iOff], &t);
|
||||
}
|
||||
|
||||
while( 1 ){
|
||||
iOff += getConstraintToken(&zSql[iOff], &t);
|
||||
if( t==TK_CONSTRAINT ){
|
||||
int nTok = 0;
|
||||
int cmp = 0;
|
||||
iOff += getWhitespace(&zSql[iOff]);
|
||||
nTok = getConstraintToken(&zSql[iOff], &t);
|
||||
if( quotedCompare(ctx, &zSql[iOff], nTok, zCons, &cmp) ) return;
|
||||
if( cmp==0 ){
|
||||
sqlite3_result_int(ctx, 1);
|
||||
return;
|
||||
}
|
||||
}else if( t==TK_ILLEGAL ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_result_int(ctx, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate bytecode to implement:
|
||||
**
|
||||
** ALTER TABLE pSrc ADD [CONSTRAINT pName] CHECK(pExpr)
|
||||
**
|
||||
** Any "ON CONFLICT" text that occurs after the "CHECK(...)", up
|
||||
** until pParse->sLastToken, is included as part of the new constraint.
|
||||
*/
|
||||
void sqlite3AlterAddConstraint(
|
||||
Parse *pParse, /* Parse context */
|
||||
SrcList *pSrc, /* Table to add constraint to */
|
||||
Token *pFirst, /* First token of new constraint */
|
||||
Token *pName, /* Name of new constraint. NULL if name omitted. */
|
||||
const char *pExpr, /* Text of CHECK expression */
|
||||
int nExpr /* Size of pExpr in bytes */
|
||||
){
|
||||
Table *pTab = 0; /* Table identified by pSrc */
|
||||
int iDb = 0; /* Which schema does pTab live in */
|
||||
const char *zDb = 0; /* Name of the schema in which pTab lives */
|
||||
const char *pCons = 0; /* Text of the constraint */
|
||||
int nCons; /* Bytes of text to use from pCons[] */
|
||||
|
||||
/* Look up the table being altered. */
|
||||
assert( pSrc->nSrc==1 );
|
||||
pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, 1);
|
||||
if( !pTab ) return;
|
||||
|
||||
/* If this new constraint has a name, check that it is not a duplicate of
|
||||
** an existing constraint. It is an error if it is. */
|
||||
if( pName ){
|
||||
char *zName = sqlite3NameFromToken(pParse->db, pName);
|
||||
|
||||
sqlite3NestedParse(pParse,
|
||||
"SELECT sqlite_fail('constraint %q already exists', %d) "
|
||||
"FROM \"%w\"." LEGACY_SCHEMA_TABLE " "
|
||||
"WHERE type='table' AND tbl_name=%Q COLLATE nocase "
|
||||
"AND sqlite_find_constraint(sql, %Q)",
|
||||
zName, SQLITE_ERROR, zDb, pTab->zName, zName
|
||||
);
|
||||
sqlite3DbFree(pParse->db, zName);
|
||||
}
|
||||
|
||||
/* Search for a constraint violation. Throw an exception if one is found. */
|
||||
sqlite3NestedParse(pParse,
|
||||
"SELECT sqlite_fail('constraint failed', %d) "
|
||||
"FROM %Q.%Q WHERE (%.*s) IS NOT TRUE",
|
||||
SQLITE_CONSTRAINT, zDb, pTab->zName, nExpr, pExpr
|
||||
);
|
||||
|
||||
/* Edit the SQL for the named table. */
|
||||
pCons = pFirst->z;
|
||||
nCons = alterRtrimConstraint(pParse->db, pCons, pParse->sLastToken.z - pCons);
|
||||
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
|
||||
"sql = sqlite_add_constraint(sql, %.*Q, -1) "
|
||||
"WHERE type='table' AND tbl_name=%Q COLLATE nocase"
|
||||
, zDb, nCons, pCons, pTab->zName
|
||||
);
|
||||
|
||||
/* Finally, reload the database schema. */
|
||||
renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons);
|
||||
}
|
||||
|
||||
/*
|
||||
** Register built-in functions used to help implement ALTER TABLE
|
||||
*/
|
||||
@@ -2323,6 +3035,10 @@ void sqlite3AlterFunctions(void){
|
||||
INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest),
|
||||
INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc),
|
||||
INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc),
|
||||
INTERNAL_FUNCTION(sqlite_drop_constraint,2, dropConstraintFunc),
|
||||
INTERNAL_FUNCTION(sqlite_fail, 2, failConstraintFunc),
|
||||
INTERNAL_FUNCTION(sqlite_add_constraint, 3, addConstraintFunc),
|
||||
INTERNAL_FUNCTION(sqlite_find_constraint,2, findConstraintFunc),
|
||||
};
|
||||
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
|
||||
}
|
||||
|
||||
@@ -5019,11 +5019,6 @@ int sqlite3SrcItemAttachSubquery(
|
||||
Subquery *p;
|
||||
assert( pSelect!=0 );
|
||||
assert( pItem->fg.isSubquery==0 );
|
||||
#if SQLITE_MAX_SUBQUERY+0 > 0
|
||||
if( pParse->nSubquery++ > SQLITE_MAX_SUBQUERY ){
|
||||
sqlite3ErrorMsg(pParse, "Too many subqueries");
|
||||
}
|
||||
#endif
|
||||
if( pItem->fg.fixedSchema ){
|
||||
pItem->u4.pSchema = 0;
|
||||
pItem->fg.fixedSchema = 0;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#if defined(_MSC_VER) && defined(_WIN32)
|
||||
|
||||
#include "windows.h"
|
||||
#include <profileapi.h>
|
||||
|
||||
__inline sqlite3_uint64 sqlite3Hwtime(void){
|
||||
|
||||
+3
-1
@@ -2930,6 +2930,7 @@ static const int aHardLimit[] = {
|
||||
SQLITE_MAX_VARIABLE_NUMBER, /* IMP: R-38091-32352 */
|
||||
SQLITE_MAX_TRIGGER_DEPTH,
|
||||
SQLITE_MAX_WORKER_THREADS,
|
||||
SQLITE_MAX_PARSER_DEPTH,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2999,6 +3000,7 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
|
||||
assert( aHardLimit[SQLITE_LIMIT_SQL_LENGTH]==SQLITE_MAX_SQL_LENGTH );
|
||||
assert( aHardLimit[SQLITE_LIMIT_COLUMN]==SQLITE_MAX_COLUMN );
|
||||
assert( aHardLimit[SQLITE_LIMIT_EXPR_DEPTH]==SQLITE_MAX_EXPR_DEPTH );
|
||||
assert( aHardLimit[SQLITE_LIMIT_PARSER_DEPTH]==SQLITE_MAX_PARSER_DEPTH );
|
||||
assert( aHardLimit[SQLITE_LIMIT_COMPOUND_SELECT]==SQLITE_MAX_COMPOUND_SELECT);
|
||||
assert( aHardLimit[SQLITE_LIMIT_VDBE_OP]==SQLITE_MAX_VDBE_OP );
|
||||
assert( aHardLimit[SQLITE_LIMIT_FUNCTION_ARG]==SQLITE_MAX_FUNCTION_ARG );
|
||||
@@ -3008,7 +3010,7 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
|
||||
assert( aHardLimit[SQLITE_LIMIT_VARIABLE_NUMBER]==SQLITE_MAX_VARIABLE_NUMBER);
|
||||
assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH );
|
||||
assert( aHardLimit[SQLITE_LIMIT_WORKER_THREADS]==SQLITE_MAX_WORKER_THREADS );
|
||||
assert( SQLITE_LIMIT_WORKER_THREADS==(SQLITE_N_LIMIT-1) );
|
||||
assert( SQLITE_LIMIT_PARSER_DEPTH==(SQLITE_N_LIMIT-1) );
|
||||
|
||||
|
||||
if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
|
||||
|
||||
+15
-25
@@ -3756,20 +3756,18 @@ int sqlite3_fullsync_count = 0;
|
||||
** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash
|
||||
** or power failure will likely corrupt the database file.
|
||||
**
|
||||
** SQLite sets the dataOnly flag if the size of the file is unchanged.
|
||||
** The idea behind dataOnly is that it should only write the file content
|
||||
** to disk, not the inode. We only set dataOnly if the file size is
|
||||
** unchanged since the file size is part of the inode. However,
|
||||
** Ted Ts'o tells us that fdatasync() will also write the inode if the
|
||||
** file size has changed. The only real difference between fdatasync()
|
||||
** and fsync(), Ted tells us, is that fdatasync() will not flush the
|
||||
** inode if the mtime or owner or other inode attributes have changed.
|
||||
** We only care about the file size, not the other file attributes, so
|
||||
** as far as SQLite is concerned, an fdatasync() is always adequate.
|
||||
** So, we always use fdatasync() if it is available, regardless of
|
||||
** the value of the dataOnly flag.
|
||||
** At one point we attempted to pass a flag to this function if the
|
||||
** file-size had not changed to tell it to use fdatasync() instead of
|
||||
** fsync() in this case. However, Ted Ts'o tells us that fdatasync() will
|
||||
** also write the inode if the file size has changed. The only real
|
||||
** difference between fdatasync() and fsync(), Ted tells us, is that
|
||||
** fdatasync() will not flush the inode if the mtime or owner or other
|
||||
** inode attributes have changed. We only care about the file size, not
|
||||
** the other file attributes, so as far as SQLite is concerned, an
|
||||
** fdatasync() is always adequate. So, we always use fdatasync() if it is
|
||||
** available.
|
||||
*/
|
||||
static int full_fsync(int fd, int fullSync, int dataOnly){
|
||||
static int full_fsync(int fd, int fullSync){
|
||||
int rc;
|
||||
|
||||
/* The following "ifdef/elif/else/" block has the same structure as
|
||||
@@ -3779,12 +3777,9 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
|
||||
#ifdef SQLITE_NO_SYNC
|
||||
UNUSED_PARAMETER(fd);
|
||||
UNUSED_PARAMETER(fullSync);
|
||||
UNUSED_PARAMETER(dataOnly);
|
||||
#elif HAVE_FULLFSYNC
|
||||
UNUSED_PARAMETER(dataOnly);
|
||||
#else
|
||||
UNUSED_PARAMETER(fullSync);
|
||||
UNUSED_PARAMETER(dataOnly);
|
||||
#endif
|
||||
|
||||
/* Record the number of times that we do a normal fsync() and
|
||||
@@ -3890,10 +3885,6 @@ static int openDirectory(const char *zFilename, int *pFd){
|
||||
/*
|
||||
** Make sure all writes to a particular file are committed to disk.
|
||||
**
|
||||
** If dataOnly==0 then both the file itself and its metadata (file
|
||||
** size, access time, etc) are synced. If dataOnly!=0 then only the
|
||||
** file data is synced.
|
||||
**
|
||||
** Under Unix, also make sure that the directory entry for the file
|
||||
** has been created by fsync-ing the directory that contains the file.
|
||||
** If we do not do this and we encounter a power failure, the directory
|
||||
@@ -3906,7 +3897,6 @@ static int unixSync(sqlite3_file *id, int flags){
|
||||
int rc;
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
|
||||
int isDataOnly = (flags&SQLITE_SYNC_DATAONLY);
|
||||
int isFullsync = (flags&0x0F)==SQLITE_SYNC_FULL;
|
||||
|
||||
/* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */
|
||||
@@ -3921,7 +3911,7 @@ static int unixSync(sqlite3_file *id, int flags){
|
||||
|
||||
assert( pFile );
|
||||
OSTRACE(("SYNC %-3d\n", pFile->h));
|
||||
rc = full_fsync(pFile->h, isFullsync, isDataOnly);
|
||||
rc = full_fsync(pFile->h, isFullsync);
|
||||
SimulateIOError( rc=1 );
|
||||
if( rc ){
|
||||
storeLastErrno(pFile, errno);
|
||||
@@ -3938,7 +3928,7 @@ static int unixSync(sqlite3_file *id, int flags){
|
||||
HAVE_FULLFSYNC, isFullsync));
|
||||
rc = osOpenDirectory(pFile->zPath, &dirfd);
|
||||
if( rc==SQLITE_OK ){
|
||||
full_fsync(dirfd, 0, 0);
|
||||
full_fsync(dirfd, 0);
|
||||
robust_close(pFile, dirfd, __LINE__);
|
||||
}else{
|
||||
assert( rc==SQLITE_CANTOPEN );
|
||||
@@ -6854,7 +6844,7 @@ static int unixDelete(
|
||||
int fd;
|
||||
rc = osOpenDirectory(zPath, &fd);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( full_fsync(fd,0,0) ){
|
||||
if( full_fsync(fd, 0) ){
|
||||
rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath);
|
||||
}
|
||||
robust_close(0, fd, __LINE__);
|
||||
@@ -7910,7 +7900,7 @@ static int proxyTakeConch(unixFile *pFile){
|
||||
writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]);
|
||||
robust_ftruncate(conchFile->h, writeSize);
|
||||
rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0);
|
||||
full_fsync(conchFile->h,0,0);
|
||||
full_fsync(conchFile->h, 0);
|
||||
/* If we created a new conch file (not just updated the contents of a
|
||||
** valid conch file), try to match the permissions of the database
|
||||
*/
|
||||
|
||||
+2
-4
@@ -1400,7 +1400,7 @@ static int zeroJournalHdr(Pager *pPager, int doTruncate){
|
||||
rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0);
|
||||
}
|
||||
if( rc==SQLITE_OK && !pPager->noSync ){
|
||||
rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags);
|
||||
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
|
||||
}
|
||||
|
||||
/* At this point the transaction is committed but the write lock
|
||||
@@ -4367,9 +4367,7 @@ static int syncJournal(Pager *pPager, int newHdr){
|
||||
if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
|
||||
PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
|
||||
IOTRACE(("JSYNC %p\n", pPager))
|
||||
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags|
|
||||
(pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
|
||||
);
|
||||
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
|
||||
|
||||
+50
-13
@@ -21,9 +21,11 @@
|
||||
*/
|
||||
}
|
||||
|
||||
// Function used to enlarge the parser stack, if needed
|
||||
%realloc parserStackRealloc
|
||||
%free sqlite3_free
|
||||
// Setup for the parser stack
|
||||
%stack_size 50 // Initial stack size
|
||||
%stack_size_limit parserStackSizeLimit // Function returning max stack size
|
||||
%realloc parserStackRealloc // realloc() for the stack
|
||||
%free parserStackFree // free() for the stack
|
||||
|
||||
// All token codes are small integers with #defines that begin with "TK_"
|
||||
%token_prefix TK_
|
||||
@@ -49,7 +51,7 @@
|
||||
}
|
||||
}
|
||||
%stack_overflow {
|
||||
sqlite3OomFault(pParse->db);
|
||||
if( pParse->nErr==0 ) sqlite3ErrorMsg(pParse, "Recursion limit");
|
||||
}
|
||||
|
||||
// The name of the generated procedure that implements the parser
|
||||
@@ -583,8 +585,23 @@ cmd ::= select(X). {
|
||||
** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate
|
||||
** testing.
|
||||
*/
|
||||
static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){
|
||||
return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize);
|
||||
static void *parserStackRealloc(
|
||||
void *pOld, /* Prior allocation */
|
||||
sqlite3_uint64 newSize, /* Requested new alloation size */
|
||||
Parse *pParse /* Parsing context */
|
||||
){
|
||||
void *p = sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize);
|
||||
if( p==0 ) sqlite3OomFault(pParse->db);
|
||||
return p;
|
||||
}
|
||||
static void parserStackFree(void *pOld, Parse *pParse){
|
||||
(void)pParse;
|
||||
sqlite3_free(pOld);
|
||||
}
|
||||
|
||||
/* Return an integer that is the maximum allowed stack size */
|
||||
static int parserStackSizeLimit(Parse *pParse){
|
||||
return pParse->db->aLimit[SQLITE_LIMIT_PARSER_DEPTH];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1831,22 +1848,42 @@ cmd ::= ANALYZE nm(X) dbnm(Y). {sqlite3Analyze(pParse, &X, &Y);}
|
||||
cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). {
|
||||
sqlite3AlterRenameTable(pParse,X,&Z);
|
||||
}
|
||||
cmd ::= ALTER TABLE add_column_fullname
|
||||
ADD kwcolumn_opt columnname(Y) carglist. {
|
||||
|
||||
// The ALTER TABLE ADD COLUMN command. This is broken into two sections so
|
||||
// that sqlite3AlterBeginAddColumn() is called before parsing the various
|
||||
// constraints and so on (carglist) attached to the new column definition.
|
||||
cmd ::= alter_add(Y) carglist. {
|
||||
Y.n = (int)(pParse->sLastToken.z-Y.z) + pParse->sLastToken.n;
|
||||
sqlite3AlterFinishAddColumn(pParse, &Y);
|
||||
}
|
||||
alter_add(A) ::= ALTER TABLE fullname(X) ADD kwcolumn_opt nm(Y) typetoken(Z). {
|
||||
disableLookaside(pParse);
|
||||
sqlite3AlterBeginAddColumn(pParse, X);
|
||||
sqlite3AddColumn(pParse, Y, Z);
|
||||
A = Y;
|
||||
}
|
||||
|
||||
cmd ::= ALTER TABLE fullname(X) DROP kwcolumn_opt nm(Y). {
|
||||
sqlite3AlterDropColumn(pParse, X, &Y);
|
||||
}
|
||||
|
||||
add_column_fullname ::= fullname(X). {
|
||||
disableLookaside(pParse);
|
||||
sqlite3AlterBeginAddColumn(pParse, X);
|
||||
}
|
||||
cmd ::= ALTER TABLE fullname(X) RENAME kwcolumn_opt nm(Y) TO nm(Z). {
|
||||
sqlite3AlterRenameColumn(pParse, X, &Y, &Z);
|
||||
}
|
||||
cmd ::= ALTER TABLE fullname(X) DROP CONSTRAINT nm(Y). {
|
||||
sqlite3AlterDropConstraint(pParse, X, &Y, 0);
|
||||
}
|
||||
cmd ::= ALTER TABLE fullname(X) ALTER kwcolumn_opt nm(Y) DROP NOT NULL. {
|
||||
sqlite3AlterDropConstraint(pParse, X, 0, &Y);
|
||||
}
|
||||
cmd ::= ALTER TABLE fullname(X) ALTER kwcolumn_opt nm(Y) SET NOT(Z) NULL onconf. {
|
||||
sqlite3AlterSetNotNull(pParse, X, &Y, &Z);
|
||||
}
|
||||
cmd ::= ALTER TABLE fullname(X) ADD CONSTRAINT(Y) nm(Z) CHECK LP(A) expr RP(B) onconf. {
|
||||
sqlite3AlterAddConstraint(pParse, X, &Y, &Z, A.z+1, (B.z-A.z-1));
|
||||
}
|
||||
cmd ::= ALTER TABLE fullname(X) ADD CHECK(Y) LP(A) expr RP(B) onconf. {
|
||||
sqlite3AlterAddConstraint(pParse, X, &Y, 0, A.z+1, (B.z-A.z-1));
|
||||
}
|
||||
|
||||
kwcolumn_opt ::= .
|
||||
kwcolumn_opt ::= COLUMNKW.
|
||||
|
||||
+2
-1
@@ -33,7 +33,8 @@ static void corruptSchema(
|
||||
static const char *azAlterType[] = {
|
||||
"rename",
|
||||
"drop column",
|
||||
"add column"
|
||||
"add column",
|
||||
"drop constraint"
|
||||
};
|
||||
*pData->pzErrMsg = sqlite3MPrintf(db,
|
||||
"error in %s %s after %s: %s", azObj[0], azObj[1],
|
||||
|
||||
+2764
-3606
File diff suppressed because it is too large
Load Diff
+6
-3
@@ -766,9 +766,7 @@ struct sqlite3_file {
|
||||
**
|
||||
** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or
|
||||
** [SQLITE_SYNC_FULL]. The first choice is the normal fsync().
|
||||
** The second choice is a Mac OS X style fullsync. The [SQLITE_SYNC_DATAONLY]
|
||||
** flag may be ORed in to indicate that only the data of the file
|
||||
** and not its inode needs to be synced.
|
||||
** The second choice is a Mac OS X style fullsync.
|
||||
**
|
||||
** The integer values to xLock() and xUnlock() are one of
|
||||
** <ul>
|
||||
@@ -4339,6 +4337,10 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt>
|
||||
** <dd>The maximum depth of the parse tree on any expression.</dd>)^
|
||||
**
|
||||
** [[SQLITE_LIMIT_PARSER_DEPTH]] ^(<dt>SQLITE_LIMIT_PARSER_DEPTH</dt>
|
||||
** <dd>The maximum depth of the LALR(1) parser stack used to analyze
|
||||
** input SQL statements.</dd>)^
|
||||
**
|
||||
** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>
|
||||
** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^
|
||||
**
|
||||
@@ -4383,6 +4385,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
#define SQLITE_LIMIT_VARIABLE_NUMBER 9
|
||||
#define SQLITE_LIMIT_TRIGGER_DEPTH 10
|
||||
#define SQLITE_LIMIT_WORKER_THREADS 11
|
||||
#define SQLITE_LIMIT_PARSER_DEPTH 12
|
||||
|
||||
/*
|
||||
** CAPI3REF: Prepare Flags
|
||||
|
||||
+6
-5
@@ -1537,7 +1537,7 @@ struct Schema {
|
||||
** The number of different kinds of things that can be limited
|
||||
** using the sqlite3_limit() interface.
|
||||
*/
|
||||
#define SQLITE_N_LIMIT (SQLITE_LIMIT_WORKER_THREADS+1)
|
||||
#define SQLITE_N_LIMIT (SQLITE_LIMIT_PARSER_DEPTH+1)
|
||||
|
||||
/*
|
||||
** Lookaside malloc is a set of fixed-size buffers that can be used
|
||||
@@ -3942,9 +3942,6 @@ struct Parse {
|
||||
yDbMask cookieMask; /* Bitmask of schema verified databases */
|
||||
int nMaxArg; /* Max args to xUpdate and xFilter vtab methods */
|
||||
int nSelect; /* Number of SELECT stmts. Counter for Select.selId */
|
||||
#if SQLITE_MAX_SUBQUERY+0 > 0
|
||||
u32 nSubquery; /* Number of subqueries seen so far */
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
u32 nProgressSteps; /* xProgress steps taken during sqlite3_prepare() */
|
||||
#endif
|
||||
@@ -4271,10 +4268,11 @@ typedef struct {
|
||||
/*
|
||||
** Allowed values for mInitFlags
|
||||
*/
|
||||
#define INITFLAG_AlterMask 0x0003 /* Types of ALTER */
|
||||
#define INITFLAG_AlterMask 0x0007 /* Types of ALTER */
|
||||
#define INITFLAG_AlterRename 0x0001 /* Reparse after a RENAME */
|
||||
#define INITFLAG_AlterDrop 0x0002 /* Reparse after a DROP COLUMN */
|
||||
#define INITFLAG_AlterAdd 0x0003 /* Reparse after an ADD COLUMN */
|
||||
#define INITFLAG_AlterDropCons 0x0004 /* Reparse after an ADD COLUMN */
|
||||
|
||||
/* Tuning parameters are set using SQLITE_TESTCTRL_TUNE and are controlled
|
||||
** on debug-builds of the CLI using ".testctrl tune ID VALUE". Tuning
|
||||
@@ -5455,6 +5453,9 @@ void sqlite3Reindex(Parse*, Token*, Token*);
|
||||
void sqlite3AlterFunctions(void);
|
||||
void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
|
||||
void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
|
||||
void sqlite3AlterDropConstraint(Parse*,SrcList*,Token*,Token*);
|
||||
void sqlite3AlterAddConstraint(Parse*,SrcList*,Token*,Token*,const char*,int);
|
||||
void sqlite3AlterSetNotNull(Parse*, SrcList*, Token*, Token*);
|
||||
i64 sqlite3GetToken(const unsigned char *, int *);
|
||||
void sqlite3NestedParse(Parse*, const char*, ...);
|
||||
void sqlite3ExpirePreparedStatements(sqlite3*, int);
|
||||
|
||||
+22
-4
@@ -66,15 +66,33 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The maximum depth of an expression tree. This is limited to
|
||||
** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
|
||||
** want to place more severe limits on the complexity of an
|
||||
** expression. A value of 0 means that there is no limit.
|
||||
** The maximum depth of an expression tree. The expression tree depth
|
||||
** is also limited indirectly by SQLITE_MAX_SQL_LENGTH and by
|
||||
** SQLITE_MAX_PARSER_DEPTH. Reducing the maximum complexity of
|
||||
** expressions can help prevent excess memory usage by hostile SQL.
|
||||
**
|
||||
** A value of 0 for this compile-time option causes all expression
|
||||
** depth limiting code to be omitted.
|
||||
*/
|
||||
#ifndef SQLITE_MAX_EXPR_DEPTH
|
||||
# define SQLITE_MAX_EXPR_DEPTH 1000
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The maximum depth of the LALR(1) stack used in the parser that
|
||||
** interprets SQL inputs. The parser stack depth can also be limited
|
||||
** indirectly by SQLITE_MAX_SQL_LENGTH. Limiting the parser stack
|
||||
** depth can help prevent excess memory usage and excess CPU stack
|
||||
** usage when processing hostile SQL.
|
||||
**
|
||||
** Prior to version 3.45.0 (2024-01-15), the parser stack was
|
||||
** hard-coded to 100 entries, and that worked fine for almost all
|
||||
** applications. So the upper bound on this limit need not be large.
|
||||
*/
|
||||
#ifndef SQLITE_MAX_PARSER_DEPTH
|
||||
# define SQLITE_MAX_PARSER_DEPTH 2500
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The maximum number of terms in a compound SELECT statement.
|
||||
** The code generator for compound SELECT statements does one
|
||||
|
||||
+389
-17
@@ -124,6 +124,15 @@
|
||||
/* Forward declaration */
|
||||
typedef struct SqliteDb SqliteDb;
|
||||
|
||||
/* Add -DSQLITE_ENABLE_QRF_IN_TCL to add the Query Result Formatter (QRF)
|
||||
** into the build of the TCL extension, when building using separate
|
||||
** source files. The QRF is included automatically when building from
|
||||
** the tclsqlite3.c amalgamation.
|
||||
*/
|
||||
#if defined(SQLITE_ENABLE_QRF_IN_TCL)
|
||||
#include "qrf.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** New SQL functions can be created as TCL scripts. Each such function
|
||||
** is described by an instance of the following structure.
|
||||
@@ -2035,6 +2044,356 @@ static void DbHookCmd(
|
||||
sqlite3_wal_hook(db, (pDb->pWalHook?DbWalHandler:0), pDb);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "db format" command.
|
||||
**
|
||||
** Based on provided options, format the results of the SQL statement(s)
|
||||
** provided into human-readable form using the Query Result Formatter (QRF)
|
||||
** and return the resuling text.
|
||||
**
|
||||
** Syntax: db format OPTIONS SQL
|
||||
**
|
||||
** OPTIONS may be:
|
||||
**
|
||||
** -style ("auto"|"box"|"column"|...) Output style
|
||||
** -esc ("auto"|"off"|"ascii"|"symbol") How to deal with ctrl chars
|
||||
** -text ("auto"|"off"|"sql"|"csv"|...) How to escape TEXT values
|
||||
** -title ("auto"|"off"|"sql"|...|"off") How to escape column names
|
||||
** -blob ("auto"|"text"|"sql"|...) How to escape BLOB values
|
||||
** -wordwrap ("auto"|"off"|"on") Try to wrap at word boundry?
|
||||
** -textjsonb ("auto"|"off"|"on") Auto-convert JSONB to text?
|
||||
** -textnull ("auto"|"off"|"on") Use text encoding for -null.
|
||||
** -splitcolumn ("auto"|"off"|"on") Enable split-column mode
|
||||
** -defaultalign ("auto"|"left"|...) Default alignment
|
||||
** -titalalign ("auto"|"left"|"right"|...) Default column name alignment
|
||||
** -wrap NUMBER Max width of any single column
|
||||
** -screenwidth NUMBER Width of the display TTY
|
||||
** -linelimit NUMBER Max lines for any cell
|
||||
** -charlimit NUMBER Content truncated to this size
|
||||
** -align LIST-OF-ALIGNMENT Alignment of columns
|
||||
** -widths LIST-OF-NUMBERS Widths for individual columns
|
||||
** -columnsep TEXT Column separator text
|
||||
** -rowsep TEXT Row separator text
|
||||
** -tablename TEXT Table name for style "insert"
|
||||
** -null TEXT Text for NULL values
|
||||
**
|
||||
** A mapping from TCL "format" command options to sqlite3_qrf_spec fields
|
||||
** is below. Use this to reference the QRF documentation:
|
||||
**
|
||||
** TCL Option spec field
|
||||
** ---------- ----------
|
||||
** -style eStyle
|
||||
** -esc eEsc
|
||||
** -text eText
|
||||
** -title eTitle, bTitle
|
||||
** -blob eBlob
|
||||
** -wordwrap bWordWrap
|
||||
** -textjsonb bTextJsonb
|
||||
** -textnull bTestNull
|
||||
** -splitcolumn bSplitColumn
|
||||
** -defaultalign eDfltAlign
|
||||
** -titlealign eTitleAlign
|
||||
** -wrap nWrap
|
||||
** -screenwidth nScreenWidth
|
||||
** -linelimit nLineLimit
|
||||
** -charlimit nCharLimit
|
||||
** -align nAlign, aAlign
|
||||
** -widths nWidth, aWidth
|
||||
** -columnsep zColumnSep
|
||||
** -rowsep zRowSep
|
||||
** -tablename zTableName
|
||||
** -null zNull
|
||||
*/
|
||||
static int dbQrf(SqliteDb *pDb, int objc, Tcl_Obj *const*objv){
|
||||
#ifndef SQLITE_QRF_H
|
||||
Tcl_SetResult(pDb->interp, "QRF not available in this build", TCL_VOLATILE);
|
||||
return TCL_ERROR;
|
||||
#else
|
||||
char *zResult = 0; /* Result to be returned */
|
||||
const char *zSql = 0; /* SQL to run */
|
||||
int i; /* Loop counter */
|
||||
int rc; /* Result code */
|
||||
sqlite3_qrf_spec qrf; /* Formatting spec */
|
||||
static const char *azAlign[] = {
|
||||
"auto", "bottom", "c",
|
||||
"center", "e", "left",
|
||||
"middle", "n", "ne",
|
||||
"nw", "right", "s",
|
||||
"se", "sw", "top",
|
||||
"w", 0
|
||||
};
|
||||
static const unsigned char aAlignMap[] = {
|
||||
QRF_ALIGN_Auto, QRF_ALIGN_Bottom, QRF_ALIGN_C,
|
||||
QRF_ALIGN_Center, QRF_ALIGN_E, QRF_ALIGN_Left,
|
||||
QRF_ALIGN_Middle, QRF_ALIGN_N, QRF_ALIGN_NE,
|
||||
QRF_ALIGN_NW, QRF_ALIGN_Right, QRF_ALIGN_S,
|
||||
QRF_ALIGN_SE, QRF_ALIGN_SW, QRF_ALIGN_Top,
|
||||
QRF_ALIGN_W
|
||||
};
|
||||
|
||||
memset(&qrf, 0, sizeof(qrf));
|
||||
qrf.iVersion = 1;
|
||||
qrf.pzOutput = &zResult;
|
||||
for(i=2; i<objc; i++){
|
||||
const char *zArg = Tcl_GetString(objv[i]);
|
||||
const char *azBool[] = { "auto", "yes", "no", "on", "off", 0 };
|
||||
const unsigned char aBoolMap[] = { 0, 2, 1, 2, 1 };
|
||||
if( zArg[0]!='-' ){
|
||||
if( zSql ){
|
||||
Tcl_AppendResult(pDb->interp, "unknown argument: ", zArg, (char*)0);
|
||||
rc = TCL_ERROR;
|
||||
goto format_failed;
|
||||
}
|
||||
zSql = zArg;
|
||||
}else if( i==objc-1 ){
|
||||
Tcl_AppendResult(pDb->interp, "option has no argument: ", zArg, (char*)0);
|
||||
rc = TCL_ERROR;
|
||||
goto format_failed;
|
||||
}else if( strcmp(zArg,"-style")==0 ){
|
||||
static const char *azStyles[] = {
|
||||
"auto", "box", "column",
|
||||
"count", "csv", "eqp",
|
||||
"explain", "html", "insert",
|
||||
"jobject", "json", "line",
|
||||
"list", "markdown", "quote",
|
||||
"stats", "stats-est", "stats-vm",
|
||||
"table", 0
|
||||
};
|
||||
static unsigned char aStyleMap[] = {
|
||||
QRF_STYLE_Auto, QRF_STYLE_Box, QRF_STYLE_Column,
|
||||
QRF_STYLE_Count, QRF_STYLE_Csv, QRF_STYLE_Eqp,
|
||||
QRF_STYLE_Explain, QRF_STYLE_Html, QRF_STYLE_Insert,
|
||||
QRF_STYLE_JObject, QRF_STYLE_Json, QRF_STYLE_Line,
|
||||
QRF_STYLE_List, QRF_STYLE_Markdown, QRF_STYLE_Quote,
|
||||
QRF_STYLE_Stats, QRF_STYLE_StatsEst, QRF_STYLE_StatsVm,
|
||||
QRF_STYLE_Table
|
||||
};
|
||||
int style;
|
||||
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azStyles,
|
||||
"format style (-style)", 0, &style);
|
||||
if( rc ) goto format_failed;
|
||||
qrf.eStyle = aStyleMap[style];
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-esc")==0 ){
|
||||
static const char *azEsc[] = {
|
||||
"ascii", "auto", "off", "symbol", 0
|
||||
};
|
||||
static unsigned char aEscMap[] = {
|
||||
QRF_ESC_Ascii, QRF_ESC_Auto, QRF_ESC_Off, QRF_ESC_Symbol
|
||||
};
|
||||
int esc;
|
||||
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azEsc,
|
||||
"control character escape (-esc)", 0, &esc);
|
||||
if( rc ) goto format_failed;
|
||||
qrf.eEsc = aEscMap[esc];
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-text")==0 || strcmp(zArg, "-title")==0 ){
|
||||
/* NB: --title can be "off" or "on but --text may not be. Thus we put
|
||||
** the "off" and "on" choices first and start the search on the
|
||||
** thrid element of the array when processing --text */
|
||||
static const char *azText[] = { "off", "on",
|
||||
"auto", "csv", "html",
|
||||
"json", "plain", "sql",
|
||||
"tcl", 0
|
||||
};
|
||||
static unsigned char aTextMap[] = {
|
||||
QRF_TEXT_Auto, QRF_TEXT_Csv, QRF_TEXT_Html,
|
||||
QRF_TEXT_Json, QRF_TEXT_Plain, QRF_TEXT_Sql,
|
||||
QRF_TEXT_Tcl
|
||||
};
|
||||
int txt;
|
||||
int k = zArg[2]=='e';
|
||||
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], &azText[k*2], zArg,
|
||||
0, &txt);
|
||||
if( rc ) goto format_failed;
|
||||
if( k ){
|
||||
qrf.eText = aTextMap[txt];
|
||||
}else if( txt<=1 ){
|
||||
qrf.bTitles = txt ? QRF_Yes : QRF_No;
|
||||
qrf.eTitle = QRF_TEXT_Auto;
|
||||
}else{
|
||||
qrf.bTitles = QRF_Yes;
|
||||
qrf.eTitle = aTextMap[txt-2];
|
||||
}
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-blob")==0 ){
|
||||
static const char *azBlob[] = {
|
||||
"auto", "hex", "json",
|
||||
"tcl", "text", "sql", 0
|
||||
};
|
||||
static unsigned char aBlobMap[] = {
|
||||
QRF_BLOB_Auto, QRF_BLOB_Hex, QRF_BLOB_Json,
|
||||
QRF_BLOB_Tcl, QRF_BLOB_Text, QRF_BLOB_Sql
|
||||
};
|
||||
int blob;
|
||||
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azBlob,
|
||||
"BLOB encoding (-blob)", 0, &blob);
|
||||
if( rc ) goto format_failed;
|
||||
qrf.eBlob = aBlobMap[blob];
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-wordwrap")==0 ){
|
||||
int v = 0;
|
||||
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azBool,
|
||||
"-wordwrap", 0, &v);
|
||||
if( rc ) goto format_failed;
|
||||
qrf.bWordWrap = aBoolMap[v];
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-textjsonb")==0
|
||||
|| strcmp(zArg,"-textnull")==0
|
||||
|| strcmp(zArg,"-splitcolumn")==0
|
||||
){
|
||||
int v = 0;
|
||||
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azBool,
|
||||
zArg, 0, &v);
|
||||
if( rc ) goto format_failed;
|
||||
if( zArg[5]=='j' ){
|
||||
qrf.bTextJsonb = aBoolMap[v];
|
||||
}else if( zArg[5]=='n' ){
|
||||
qrf.bTextNull = aBoolMap[v];
|
||||
}else{
|
||||
qrf.bSplitColumn = aBoolMap[v];
|
||||
}
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-defaultalign")==0 || strcmp(zArg,"-titlealign")==0){
|
||||
int ax = 0;
|
||||
rc = Tcl_GetIndexFromObj(pDb->interp, objv[i+1], azAlign,
|
||||
zArg[1]=='d' ? "default alignment (-defaultalign)" :
|
||||
"title alignment (-titlealign)",
|
||||
0, &ax);
|
||||
if( rc ) goto format_failed;
|
||||
if( zArg[1]=='d' ){
|
||||
qrf.eDfltAlign = aAlignMap[ax];
|
||||
}else{
|
||||
qrf.eTitleAlign = aAlignMap[ax];
|
||||
}
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-wrap")==0
|
||||
|| strcmp(zArg,"-screenwidth")==0
|
||||
|| strcmp(zArg,"-linelimit")==0
|
||||
){
|
||||
int v = 0;
|
||||
rc = Tcl_GetIntFromObj(pDb->interp, objv[i+1], &v);
|
||||
if( rc ) goto format_failed;
|
||||
if( v<QRF_MIN_WIDTH ){
|
||||
v = QRF_MIN_WIDTH;
|
||||
}else if( v>QRF_MAX_WIDTH ){
|
||||
v = QRF_MAX_WIDTH;
|
||||
}
|
||||
if( zArg[1]=='w' ){
|
||||
qrf.nWrap = v;
|
||||
}else if( zArg[1]=='s' ){
|
||||
qrf.nScreenWidth = v;
|
||||
}else{
|
||||
qrf.nLineLimit = v;
|
||||
}
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-charlimit")==0 ){
|
||||
int v = 0;
|
||||
rc = Tcl_GetIntFromObj(pDb->interp, objv[i+1], &v);
|
||||
if( rc ) goto format_failed;
|
||||
if( v<0 ) v = 0;
|
||||
qrf.nCharLimit = v;
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-align")==0 ){
|
||||
Tcl_Size n = 0;
|
||||
int jj;
|
||||
rc = Tcl_ListObjLength(pDb->interp, objv[i+1], &n);
|
||||
if( rc ) goto format_failed;
|
||||
sqlite3_free(qrf.aAlign);
|
||||
qrf.aAlign = sqlite3_malloc64( (n+1)*sizeof(qrf.aAlign[0]) );
|
||||
if( qrf.aAlign==0 ){
|
||||
Tcl_AppendResult(pDb->interp, "out of memory", (char*)0);
|
||||
rc = TCL_ERROR;
|
||||
goto format_failed;
|
||||
}
|
||||
memset(qrf.aAlign, 0, (n+1)*sizeof(qrf.aAlign[0]));
|
||||
qrf.nAlign = n;
|
||||
for(jj=0; jj<n; jj++){
|
||||
int x;
|
||||
Tcl_Obj *pTerm;
|
||||
rc = Tcl_ListObjIndex(pDb->interp, objv[i+1], jj, &pTerm);
|
||||
if( rc ) goto format_failed;
|
||||
rc = Tcl_GetIndexFromObj(pDb->interp, pTerm, azAlign,
|
||||
"column alignment (-align)", 0, &x);
|
||||
if( rc ) goto format_failed;
|
||||
qrf.aAlign[jj] = aAlignMap[x];
|
||||
}
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-widths")==0 ){
|
||||
Tcl_Size n = 0;
|
||||
int jj;
|
||||
rc = Tcl_ListObjLength(pDb->interp, objv[i+1], &n);
|
||||
if( rc ) goto format_failed;
|
||||
sqlite3_free(qrf.aWidth);
|
||||
qrf.aWidth = sqlite3_malloc64( (n+1)*sizeof(qrf.aWidth[0]) );
|
||||
if( qrf.aWidth==0 ){
|
||||
Tcl_AppendResult(pDb->interp, "out of memory", (char*)0);
|
||||
rc = TCL_ERROR;
|
||||
goto format_failed;
|
||||
}
|
||||
memset(qrf.aWidth, 0, (n+1)*sizeof(qrf.aWidth[0]));
|
||||
qrf.nWidth = n;
|
||||
for(jj=0; jj<n; jj++){
|
||||
Tcl_Obj *pTerm;
|
||||
int v;
|
||||
rc = Tcl_ListObjIndex(pDb->interp, objv[i+1], jj, &pTerm);
|
||||
if( rc ) goto format_failed;
|
||||
rc = Tcl_GetIntFromObj(pDb->interp, pTerm, &v);
|
||||
if( v<(-QRF_MAX_WIDTH) ){
|
||||
v = -QRF_MAX_WIDTH;
|
||||
}else if( v>QRF_MAX_WIDTH ){
|
||||
v = QRF_MAX_WIDTH;
|
||||
}
|
||||
qrf.aWidth[jj] = (short int)v;
|
||||
}
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-columnsep")==0 ){
|
||||
qrf.zColumnSep = Tcl_GetString(objv[i+1]);
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-rowsep")==0 ){
|
||||
qrf.zRowSep = Tcl_GetString(objv[i+1]);
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-tablename")==0 ){
|
||||
qrf.zTableName = Tcl_GetString(objv[i+1]);
|
||||
i++;
|
||||
}else if( strcmp(zArg,"-null")==0 ){
|
||||
qrf.zNull = Tcl_GetString(objv[i+1]);
|
||||
i++;
|
||||
}else{
|
||||
Tcl_AppendResult(pDb->interp, "unknown option: ", zArg, (char*)0);
|
||||
rc = TCL_ERROR;
|
||||
goto format_failed;
|
||||
}
|
||||
}
|
||||
while( zSql && zSql[0] ){
|
||||
SqlPreparedStmt *pStmt = 0; /* Next statement to run */
|
||||
char *zErr = 0; /* Error message from QRF */
|
||||
|
||||
rc = dbPrepareAndBind(pDb, zSql, &zSql, &pStmt);
|
||||
if( rc ) goto format_failed;
|
||||
if( pStmt==0 ) continue;
|
||||
rc = sqlite3_format_query_result(pStmt->pStmt, &qrf, &zErr);
|
||||
dbReleaseStmt(pDb, pStmt, 0);
|
||||
if( rc ){
|
||||
Tcl_SetResult(pDb->interp, zErr, TCL_VOLATILE);
|
||||
sqlite3_free(zErr);
|
||||
rc = TCL_ERROR;
|
||||
goto format_failed;
|
||||
}
|
||||
}
|
||||
Tcl_SetResult(pDb->interp, zResult, TCL_VOLATILE);
|
||||
rc = TCL_OK;
|
||||
/* Fall through...*/
|
||||
|
||||
format_failed:
|
||||
sqlite3_free(qrf.aWidth);
|
||||
sqlite3_free(qrf.aAlign);
|
||||
sqlite3_free(zResult);
|
||||
return rc;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** The "sqlite" command below creates a new Tcl command for each
|
||||
** connection it opens to an SQLite database. This routine is invoked
|
||||
@@ -2064,15 +2423,15 @@ static int SQLITE_TCLAPI DbObjCmd(
|
||||
"commit_hook", "complete", "config",
|
||||
"copy", "deserialize", "enable_load_extension",
|
||||
"errorcode", "erroroffset", "eval",
|
||||
"exists", "function", "incrblob",
|
||||
"interrupt", "last_insert_rowid", "nullvalue",
|
||||
"onecolumn", "preupdate", "profile",
|
||||
"progress", "rekey", "restore",
|
||||
"rollback_hook", "serialize", "status",
|
||||
"timeout", "total_changes", "trace",
|
||||
"trace_v2", "transaction", "unlock_notify",
|
||||
"update_hook", "version", "wal_hook",
|
||||
0
|
||||
"exists", "format", "function",
|
||||
"incrblob", "interrupt", "last_insert_rowid",
|
||||
"nullvalue", "onecolumn", "preupdate",
|
||||
"profile", "progress", "rekey",
|
||||
"restore", "rollback_hook", "serialize",
|
||||
"status", "timeout", "total_changes",
|
||||
"trace", "trace_v2", "transaction",
|
||||
"unlock_notify", "update_hook", "version",
|
||||
"wal_hook", 0
|
||||
};
|
||||
enum DB_enum {
|
||||
DB_AUTHORIZER, DB_BACKUP, DB_BIND_FALLBACK,
|
||||
@@ -2081,14 +2440,15 @@ static int SQLITE_TCLAPI DbObjCmd(
|
||||
DB_COMMIT_HOOK, DB_COMPLETE, DB_CONFIG,
|
||||
DB_COPY, DB_DESERIALIZE, DB_ENABLE_LOAD_EXTENSION,
|
||||
DB_ERRORCODE, DB_ERROROFFSET, DB_EVAL,
|
||||
DB_EXISTS, DB_FUNCTION, DB_INCRBLOB,
|
||||
DB_INTERRUPT, DB_LAST_INSERT_ROWID, DB_NULLVALUE,
|
||||
DB_ONECOLUMN, DB_PREUPDATE, DB_PROFILE,
|
||||
DB_PROGRESS, DB_REKEY, DB_RESTORE,
|
||||
DB_ROLLBACK_HOOK, DB_SERIALIZE, DB_STATUS,
|
||||
DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE,
|
||||
DB_TRACE_V2, DB_TRANSACTION, DB_UNLOCK_NOTIFY,
|
||||
DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK,
|
||||
DB_EXISTS, DB_FORMAT, DB_FUNCTION,
|
||||
DB_INCRBLOB, DB_INTERRUPT, DB_LAST_INSERT_ROWID,
|
||||
DB_NULLVALUE, DB_ONECOLUMN, DB_PREUPDATE,
|
||||
DB_PROFILE, DB_PROGRESS, DB_REKEY,
|
||||
DB_RESTORE, DB_ROLLBACK_HOOK, DB_SERIALIZE,
|
||||
DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES,
|
||||
DB_TRACE, DB_TRACE_V2, DB_TRANSACTION,
|
||||
DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION,
|
||||
DB_WAL_HOOK
|
||||
};
|
||||
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
|
||||
|
||||
@@ -2978,6 +3338,18 @@ deserialize_error:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** $db format [OPTIONS] SQL
|
||||
**
|
||||
** Run the SQL statement(s) given as the final argument. Use the
|
||||
** Query Result Formatter extension of SQLite to format the output as
|
||||
** text and return that text.
|
||||
*/
|
||||
case DB_FORMAT: {
|
||||
rc = dbQrf(pDb, objc, objv);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** $db function NAME [OPTIONS] SCRIPT
|
||||
**
|
||||
|
||||
+2
-1
@@ -7370,6 +7370,7 @@ static int SQLITE_TCLAPI test_limit(
|
||||
{ "SQLITE_LIMIT_SQL_LENGTH", SQLITE_LIMIT_SQL_LENGTH },
|
||||
{ "SQLITE_LIMIT_COLUMN", SQLITE_LIMIT_COLUMN },
|
||||
{ "SQLITE_LIMIT_EXPR_DEPTH", SQLITE_LIMIT_EXPR_DEPTH },
|
||||
{ "SQLITE_LIMIT_PARSER_DEPTH", SQLITE_LIMIT_PARSER_DEPTH },
|
||||
{ "SQLITE_LIMIT_COMPOUND_SELECT", SQLITE_LIMIT_COMPOUND_SELECT },
|
||||
{ "SQLITE_LIMIT_VDBE_OP", SQLITE_LIMIT_VDBE_OP },
|
||||
{ "SQLITE_LIMIT_FUNCTION_ARG", SQLITE_LIMIT_FUNCTION_ARG },
|
||||
@@ -7381,7 +7382,7 @@ static int SQLITE_TCLAPI test_limit(
|
||||
|
||||
/* Out of range test cases */
|
||||
{ "SQLITE_LIMIT_TOOSMALL", -1, },
|
||||
{ "SQLITE_LIMIT_TOOBIG", SQLITE_LIMIT_WORKER_THREADS+1 },
|
||||
{ "SQLITE_LIMIT_TOOBIG", SQLITE_LIMIT_PARSER_DEPTH+1 },
|
||||
};
|
||||
int i, id = 0;
|
||||
int val;
|
||||
|
||||
+1
-1
@@ -514,7 +514,7 @@ static int fsSync(sqlite3_file *pFile, int flags){
|
||||
rc = pRealFile->pMethods->xWrite(pRealFile, zSize, 4, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pRealFile->pMethods->xSync(pRealFile, flags&(~SQLITE_SYNC_DATAONLY));
|
||||
rc = pRealFile->pMethods->xSync(pRealFile, flags);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
@@ -436,12 +436,6 @@ static int tvfsSync(sqlite3_file *pFile, int flags){
|
||||
case SQLITE_SYNC_FULL:
|
||||
zFlags = "full";
|
||||
break;
|
||||
case SQLITE_SYNC_NORMAL|SQLITE_SYNC_DATAONLY:
|
||||
zFlags = "normal|dataonly";
|
||||
break;
|
||||
case SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY:
|
||||
zFlags = "full|dataonly";
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -36,6 +36,40 @@ do_faultsim_test 1.1 -faults oom* -prep {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE x1(d, e CONSTRAINT abc NOT NULL, f);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
foreach {tn sql} {
|
||||
1 { ALTER TABLE x1 ADD CHECK (d!=1) }
|
||||
2 { ALTER TABLE x1 ADD CONSTRAINT xyz CHECK (f>d+e); }
|
||||
3 { ALTER TABLE x1 DROP CONSTRAINT abc }
|
||||
4 { ALTER TABLE x1 ALTER f SET NOT NULL }
|
||||
5 { ALTER TABLE x1 ALTER e DROP NOT NULL }
|
||||
} {
|
||||
do_faultsim_test 2.$tn -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql $::sql
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
}
|
||||
|
||||
# Test an OOM when returning an error.
|
||||
#
|
||||
do_faultsim_test 2.e -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
ALTER TABLE x1 DROP CONSTRAINT nosuchconstraint
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result \
|
||||
{1 {no such constraint: nosuchconstraint}} \
|
||||
{1 {SQL logic error}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -332,9 +332,11 @@ do_test 4.3 {
|
||||
set ofd [open $shdo w]
|
||||
if {$::cliDoesAr} {
|
||||
puts $ofd ".ar -u $shdo"
|
||||
puts $ofd ".mode list"
|
||||
puts $ofd "select count(*) from sqlar where name = '$shdo';"
|
||||
} else {
|
||||
puts $ofd "insert into sqlar values (1);"
|
||||
puts $ofd ".mode list"
|
||||
puts $ofd "select count(*) from sqlar;"
|
||||
}
|
||||
puts $ofd ".q"
|
||||
|
||||
+1
-1
@@ -595,7 +595,7 @@ do_test misc5-7.1.2 {
|
||||
}
|
||||
append sql "0$tail); SELECT * FROM t1;"
|
||||
catchsql $sql
|
||||
} {0 900}
|
||||
} {1 {Recursion limit}}
|
||||
|
||||
|
||||
# Parser stack overflow is silently ignored when it occurs while parsing the
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# 2025-11-12
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test cases for the ".mode" command of the CLI.
|
||||
# To run these tests:
|
||||
#
|
||||
# ./sqlite3 <test/modeA.clitest
|
||||
#
|
||||
#
|
||||
CREATE TABLE t1(a,b,c,d,e);
|
||||
INSERT INTO t1 VALUES(1,2.5,'three',x'4444',NULL);
|
||||
INSERT INTO t1 SELECT b,c,d,e,a FROM t1;
|
||||
INSERT INTO t1 SELECT d,e,a,b,c FROM t1;
|
||||
.mode box
|
||||
|
||||
.output memory
|
||||
SELECT * FROM t1;
|
||||
.output --verify END
|
||||
┌─────┬───────┬───────┬───────┬───────┐
|
||||
│ a │ b │ c │ d │ e │
|
||||
├─────┼───────┼───────┼───────┼───────┤
|
||||
│ 1 │ 2.5 │ three │ DD │ │
|
||||
│ 2.5 │ three │ DD │ │ 1 │
|
||||
│ DD │ │ 1 │ 2.5 │ three │
|
||||
│ │ 1 │ 2.5 │ three │ DD │
|
||||
└─────┴───────┴───────┴───────┴───────┘
|
||||
END
|
||||
|
||||
.output memory
|
||||
.mode --null xyz
|
||||
SELECT * FROM t1;
|
||||
.output --verify END
|
||||
┌─────┬───────┬───────┬───────┬───────┐
|
||||
│ a │ b │ c │ d │ e │
|
||||
├─────┼───────┼───────┼───────┼───────┤
|
||||
│ 1 │ 2.5 │ three │ DD │ xyz │
|
||||
│ 2.5 │ three │ DD │ xyz │ 1 │
|
||||
│ DD │ xyz │ 1 │ 2.5 │ three │
|
||||
│ xyz │ 1 │ 2.5 │ three │ DD │
|
||||
└─────┴───────┴───────┴───────┴───────┘
|
||||
END
|
||||
|
||||
.output memory --error-prefix "Error:"
|
||||
.mode foo
|
||||
.output --verify END
|
||||
Error: .mode foo
|
||||
Error: ^--- unknown mode
|
||||
Error: Use ".help .mode" for more info
|
||||
END
|
||||
|
||||
.output memory
|
||||
.mode --null xyzzy -v
|
||||
.output -glob ' --null "xyzzy"'
|
||||
.output memory
|
||||
.mode -null abcde -v
|
||||
.output -glob ' --null "abcde"'
|
||||
|
||||
# Test cases for the ".explain off" command
|
||||
.mode box -reset
|
||||
.output memory
|
||||
EXPLAIN SELECT * FROM t1;
|
||||
.output --notglob *────* --keep
|
||||
.output --notglob "* id │ parent │ notused │ detail *" --keep
|
||||
.output --glob "* Init *"
|
||||
.output memory
|
||||
EXPLAIN QUERY PLAN SELECT * FROM t1;
|
||||
.output --glob "*`--SCAN *"
|
||||
.explain off
|
||||
.output memory
|
||||
EXPLAIN SELECT * FROM t1;
|
||||
.output --glob *────*
|
||||
.output memory
|
||||
EXPLAIN QUERY PLAN SELECT * FROM t1;
|
||||
.output --glob "* id │ parent │ notused │ detail *"
|
||||
.explain auto
|
||||
+994
@@ -0,0 +1,994 @@
|
||||
# 2025-11-05
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test cases for the Query Result Formatter (QRF)
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix qrf01
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES(1,2.5,'three'),(x'424c4f42',NULL,'Ἀμήν');
|
||||
}
|
||||
|
||||
do_test 1.10 {
|
||||
set result "\n[db format {SELECT * FROM t1}]"
|
||||
} {
|
||||
┌──────┬─────┬───────┐
|
||||
│ a │ b │ c │
|
||||
├──────┼─────┼───────┤
|
||||
│ 1 │ 2.5 │ three │
|
||||
│ BLOB │ │ Ἀμήν │
|
||||
└──────┴─────┴───────┘
|
||||
}
|
||||
do_test 1.11a {
|
||||
set result "\n[db format -title off {SELECT * FROM t1}]"
|
||||
} {
|
||||
┌──────┬─────┬───────┐
|
||||
│ 1 │ 2.5 │ three │
|
||||
│ BLOB │ │ Ἀμήν │
|
||||
└──────┴─────┴───────┘
|
||||
}
|
||||
do_test 1.11b {
|
||||
set result "\n[db format -text sql {SELECT * FROM t1}]"
|
||||
} {
|
||||
┌─────────────┬─────┬─────────┐
|
||||
│ a │ b │ c │
|
||||
├─────────────┼─────┼─────────┤
|
||||
│ 1 │ 2.5 │ 'three' │
|
||||
│ x'424c4f42' │ │ 'Ἀμήν' │
|
||||
└─────────────┴─────┴─────────┘
|
||||
}
|
||||
do_test 1.12 {
|
||||
set result "\n[db format -text csv {SELECT * FROM t1}]"
|
||||
} {
|
||||
┌────────────────────┬─────┬────────┐
|
||||
│ a │ b │ c │
|
||||
├────────────────────┼─────┼────────┤
|
||||
│ 1 │ 2.5 │ three │
|
||||
│ "\102\114\117\102" │ │ "Ἀμήν" │
|
||||
└────────────────────┴─────┴────────┘
|
||||
}
|
||||
do_test 1.13 {
|
||||
set result "\n[db format -text csv -blob hex {SELECT * FROM t1}]"
|
||||
} {
|
||||
┌──────────┬─────┬────────┐
|
||||
│ a │ b │ c │
|
||||
├──────────┼─────┼────────┤
|
||||
│ 1 │ 2.5 │ three │
|
||||
│ 424c4f42 │ │ "Ἀμήν" │
|
||||
└──────────┴─────┴────────┘
|
||||
}
|
||||
do_test 1.14 {
|
||||
catch {db format -text unk -blob hex {SELECT * FROM t1}} res
|
||||
set res
|
||||
} {bad -text "unk": must be auto, csv, html, json, plain, sql, or tcl}
|
||||
do_test 1.15 {
|
||||
catch {db format -text sql -blob unk {SELECT * FROM t1}} res
|
||||
set res
|
||||
} {bad BLOB encoding (-blob) "unk": must be auto, hex, json, tcl, text, or sql}
|
||||
do_test 1.16 {
|
||||
catch {db format -text sql -style unk {SELECT * FROM t1}} res
|
||||
set res
|
||||
} {bad format style (-style) "unk": must be auto, box, column, count, csv, eqp, explain, html, insert, jobject, json, line, list, markdown, quote, stats, stats-est, stats-vm, or table}
|
||||
|
||||
|
||||
do_test 1.20 {
|
||||
set result "\n[db format -style box {SELECT * FROM t1}]"
|
||||
} {
|
||||
┌──────┬─────┬───────┐
|
||||
│ a │ b │ c │
|
||||
├──────┼─────┼───────┤
|
||||
│ 1 │ 2.5 │ three │
|
||||
│ BLOB │ │ Ἀμήν │
|
||||
└──────┴─────┴───────┘
|
||||
}
|
||||
|
||||
do_test 1.30 {
|
||||
set result "\n[db format -style table {SELECT * FROM t1}]"
|
||||
} {
|
||||
+------+-----+-------+
|
||||
| a | b | c |
|
||||
+------+-----+-------+
|
||||
| 1 | 2.5 | three |
|
||||
| BLOB | | Ἀμήν |
|
||||
+------+-----+-------+
|
||||
}
|
||||
do_test 1.31 {
|
||||
set result "\n[db format -style table -title off {SELECT * FROM t1}]"
|
||||
} {
|
||||
+------+-----+-------+
|
||||
| 1 | 2.5 | three |
|
||||
| BLOB | | Ἀμήν |
|
||||
+------+-----+-------+
|
||||
}
|
||||
|
||||
do_test 1.40 {
|
||||
set result "\n[db format -style column {SELECT * FROM t1}]"
|
||||
} {
|
||||
a b c
|
||||
---- --- -----
|
||||
1 2.5 three
|
||||
BLOB Ἀμήν
|
||||
}
|
||||
do_test 1.41 {
|
||||
set result "\n[db format -style column -title off {SELECT * FROM t1}]"
|
||||
} {
|
||||
1 2.5 three
|
||||
BLOB Ἀμήν
|
||||
}
|
||||
|
||||
do_test 1.50 {
|
||||
db format -style count {SELECT * FROM t1}
|
||||
} 2
|
||||
|
||||
do_test 1.60a {
|
||||
db format -style list -columnsep , -rowsep \r\n -text csv -blob tcl {SELECT * FROM t1}
|
||||
} "1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"Ἀμήν\"\r\n"
|
||||
do_test 1.60b {
|
||||
db format -style csv -columnsep xyz -rowsep pqr -text sql -blob sql {SELECT * FROM t1}
|
||||
} "1,2.5,three\r\nBLOB,,\"Ἀμήν\"\r\n"
|
||||
do_test 1.61a {
|
||||
db format -style list -columnsep , -rowsep \r\n -text csv -title auto -blob tcl {SELECT * FROM t1}
|
||||
} "a,b,c\r\n1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"Ἀμήν\"\r\n"
|
||||
do_test 1.61b {
|
||||
db format -style csv -title auto -blob tcl {SELECT * FROM t1}
|
||||
} "a,b,c\r\n1,2.5,three\r\nBLOB,,\"Ἀμήν\"\r\n"
|
||||
do_test 1.62a {
|
||||
db format -style list -columnsep , -rowsep \r\n -text csv -title csv -blob tcl {SELECT a AS 'a x y', b, c FROM t1}
|
||||
} "\"a x y\",b,c\r\n1,2.5,three\r\n\"\\102\\114\\117\\102\",,\"Ἀμήν\"\r\n"
|
||||
do_test 1.62b {
|
||||
db format -style csv -title csv -blob tcl {SELECT a AS 'a x y', b, c FROM t1}
|
||||
} "\"a x y\",b,c\r\n1,2.5,three\r\nBLOB,,\"Ἀμήν\"\r\n"
|
||||
|
||||
do_test 1.70 {
|
||||
set result "\n[db format -style html {SELECT * FROM t1}]"
|
||||
} {
|
||||
<TR>
|
||||
<TD>1
|
||||
<TD>2.5
|
||||
<TD>three
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>BLOB
|
||||
<TD>null
|
||||
<TD>Ἀμήν
|
||||
</TR>
|
||||
}
|
||||
do_test 1.71 {
|
||||
set result "\n[db format -style html -title auto {SELECT * FROM t1}]"
|
||||
} {
|
||||
<TR>
|
||||
<TH>a
|
||||
<TH>b
|
||||
<TH>c
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>1
|
||||
<TD>2.5
|
||||
<TD>three
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>BLOB
|
||||
<TD>null
|
||||
<TD>Ἀμήν
|
||||
</TR>
|
||||
}
|
||||
|
||||
do_test 1.80 {
|
||||
set result "\n[db format -style insert {SELECT * FROM t1}]"
|
||||
} {
|
||||
INSERT INTO tab VALUES(1,2.5,'three');
|
||||
INSERT INTO tab VALUES(x'424c4f42',NULL,'Ἀμήν');
|
||||
}
|
||||
do_test 1.81 {
|
||||
set result "\n[db format -style insert -tablename t1 {SELECT * FROM t1}]"
|
||||
} {
|
||||
INSERT INTO t1 VALUES(1,2.5,'three');
|
||||
INSERT INTO t1 VALUES(x'424c4f42',NULL,'Ἀμήν');
|
||||
}
|
||||
do_test 1.82 {
|
||||
set result "\n[db format -style insert -tablename t1 -title auto \
|
||||
{SELECT * FROM t1}]"
|
||||
} {
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2.5,'three');
|
||||
INSERT INTO t1(a,b,c) VALUES(x'424c4f42',NULL,'Ἀμήν');
|
||||
}
|
||||
do_test 1.83 {
|
||||
set result "\n[db format -style insert -tablename drop -title on \
|
||||
{SELECT a AS "a-b", b, c AS "123" FROM t1}]"
|
||||
} {
|
||||
INSERT INTO "drop"("a-b",b,"123") VALUES(1,2.5,'three');
|
||||
INSERT INTO "drop"("a-b",b,"123") VALUES(x'424c4f42',NULL,'Ἀμήν');
|
||||
}
|
||||
|
||||
do_test 1.90 {
|
||||
set result "\n[db format -style json {SELECT * FROM t1}]"
|
||||
} {
|
||||
[{"a":1,"b":2.5,"c":"three"},
|
||||
{"a":"\u0042\u004c\u004f\u0042","b":null,"c":"Ἀμήν"}]
|
||||
}
|
||||
do_test 1.91 {
|
||||
set result "\n[db format -style jobject {SELECT * FROM t1}]"
|
||||
} {
|
||||
{"a":1,"b":2.5,"c":"three"}
|
||||
{"a":"\u0042\u004c\u004f\u0042","b":null,"c":"Ἀμήν"}
|
||||
}
|
||||
do_test 1.92 {
|
||||
set result "\n[db format -style jobject {SELECT *, unistr('abc\u000a123\u000d\u000axyz') AS xyz FROM t1}]"
|
||||
} {
|
||||
{"a":1,"b":2.5,"c":"three","xyz":"abc\n123\r\nxyz"}
|
||||
{"a":"\u0042\u004c\u004f\u0042","b":null,"c":"Ἀμήν","xyz":"abc\n123\r\nxyz"}
|
||||
}
|
||||
|
||||
do_test 1.100 {
|
||||
set result "\n[db format -style line {SELECT * FROM t1}]"
|
||||
} {
|
||||
a = 1
|
||||
b = 2.5
|
||||
c = three
|
||||
|
||||
a = BLOB
|
||||
b =
|
||||
c = Ἀμήν
|
||||
}
|
||||
do_test 1.101 {
|
||||
set result "\n[db format -style line -null (NULL) {SELECT * FROM t1}]"
|
||||
} {
|
||||
a = 1
|
||||
b = 2.5
|
||||
c = three
|
||||
|
||||
a = BLOB
|
||||
b = (NULL)
|
||||
c = Ἀμήν
|
||||
}
|
||||
do_test 1.102 {
|
||||
set result "\n[db format -style line -null (NULL) \
|
||||
-text sql {SELECT * FROM t1}]"
|
||||
} {
|
||||
a = 1
|
||||
b = 2.5
|
||||
c = 'three'
|
||||
|
||||
a = x'424c4f42'
|
||||
b = (NULL)
|
||||
c = 'Ἀμήν'
|
||||
}
|
||||
|
||||
do_test 1.110 {
|
||||
set result "\n[db format -style list {SELECT * FROM t1}]"
|
||||
} {
|
||||
1|2.5|three
|
||||
BLOB||Ἀμήν
|
||||
}
|
||||
do_test 1.111 {
|
||||
set result "\n[db format -style list -title on {SELECT * FROM t1}]"
|
||||
} {
|
||||
a|b|c
|
||||
1|2.5|three
|
||||
BLOB||Ἀμήν
|
||||
}
|
||||
do_test 1.112 {
|
||||
set result "\n[db format -style list -title on -text sql -null NULL \
|
||||
-title plain {SELECT * FROM t1}]"
|
||||
} {
|
||||
a|b|c
|
||||
1|2.5|'three'
|
||||
x'424c4f42'|NULL|'Ἀμήν'
|
||||
}
|
||||
do_test 1.118 {
|
||||
set rc [catch {db format -style list -title unk {SELECT * FROM t1}} res]
|
||||
lappend rc $res
|
||||
} {1 {bad -title "unk": must be off, on, auto, csv, html, json, plain, sql, or tcl}}
|
||||
|
||||
|
||||
do_test 1.120 {
|
||||
set result "\n[db format -style markdown {SELECT * FROM t1}]"
|
||||
} {
|
||||
| a | b | c |
|
||||
|------|-----|-------|
|
||||
| 1 | 2.5 | three |
|
||||
| BLOB | | Ἀμήν |
|
||||
}
|
||||
do_test 1.121 {
|
||||
set result "\n[db format -style markdown -title off {SELECT * FROM t1}]"
|
||||
} {
|
||||
| 1 | 2.5 | three |
|
||||
| BLOB | | Ἀμήν |
|
||||
}
|
||||
|
||||
do_test 1.130 {
|
||||
set result "\n[db format -style quote {SELECT * FROM t1}]"
|
||||
} {
|
||||
1,2.5,'three'
|
||||
x'424c4f42',NULL,'Ἀμήν'
|
||||
}
|
||||
do_test 1.131 {
|
||||
set result "\n[db format -style quote -title on {SELECT * FROM t1}]"
|
||||
} {
|
||||
'a','b','c'
|
||||
1,2.5,'three'
|
||||
x'424c4f42',NULL,'Ἀμήν'
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(1,2,'The quick fox jumps over the lazy brown dog.');
|
||||
}
|
||||
do_test 2.1 {
|
||||
set result "\n[db format -widths {5 -5 19} -wordwrap on \
|
||||
{SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───────┬───────┬─────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───────┼───────┼─────────────────────┤
|
||||
│ 1 │ 2 │ The quick fox jumps │
|
||||
│ │ │ over the lazy brown │
|
||||
│ │ │ dog. │
|
||||
└───────┴───────┴─────────────────────┘
|
||||
}
|
||||
do_test 2.2 {
|
||||
set result "\n[db format -widths {5 -5 19} -wordwrap off \
|
||||
{SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───────┬───────┬─────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───────┼───────┼─────────────────────┤
|
||||
│ 1 │ 2 │ The quick fox jumps │
|
||||
│ │ │ over the lazy brown │
|
||||
│ │ │ dog. │
|
||||
└───────┴───────┴─────────────────────┘
|
||||
}
|
||||
do_test 2.3 {
|
||||
set result "\n[db format -widths {5 -5 18} -wordwrap on \
|
||||
{SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───────┬───────┬────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───────┼───────┼────────────────────┤
|
||||
│ 1 │ 2 │ The quick fox │
|
||||
│ │ │ jumps over the │
|
||||
│ │ │ lazy brown dog. │
|
||||
└───────┴───────┴────────────────────┘
|
||||
}
|
||||
do_test 2.4 {
|
||||
set result "\n[db format -widths {5 -5 -18} -wordwrap on \
|
||||
{SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───────┬───────┬────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───────┼───────┼────────────────────┤
|
||||
│ 1 │ 2 │ The quick fox │
|
||||
│ │ │ jumps over the │
|
||||
│ │ │ lazy brown dog. │
|
||||
└───────┴───────┴────────────────────┘
|
||||
}
|
||||
do_test 2.5 {
|
||||
set result "\n[db format -widths {5 -5 19} -wordwrap off \
|
||||
{SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───────┬───────┬─────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───────┼───────┼─────────────────────┤
|
||||
│ 1 │ 2 │ The quick fox jumps │
|
||||
│ │ │ over the lazy brown │
|
||||
│ │ │ dog. │
|
||||
└───────┴───────┴─────────────────────┘
|
||||
}
|
||||
do_test 2.6 {
|
||||
set result "\n[db format -widths {5 -5 18} -wordwrap off \
|
||||
{SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───────┬───────┬────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───────┼───────┼────────────────────┤
|
||||
│ 1 │ 2 │ The quick fox jump │
|
||||
│ │ │ s over the lazy br │
|
||||
│ │ │ own dog. │
|
||||
└───────┴───────┴────────────────────┘
|
||||
}
|
||||
do_test 2.7 {
|
||||
set result "\n[db format -widths {5 5 18} -wordwrap yes \
|
||||
-align {left center right} -titlealign right \
|
||||
{SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───────┬───────┬────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───────┼───────┼────────────────────┤
|
||||
│ 1 │ 2 │ The quick fox │
|
||||
│ │ │ jumps over the │
|
||||
│ │ │ lazy brown dog. │
|
||||
└───────┴───────┴────────────────────┘
|
||||
}
|
||||
do_test 2.8 {
|
||||
set result "\n[db format -widths {5 8 11} -wordwrap yes \
|
||||
-align {auto auto center} -titlealign left \
|
||||
-defaultalign right \
|
||||
{SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───────┬──────────┬─────────────┐
|
||||
│ a │ b │ c │
|
||||
├───────┼──────────┼─────────────┤
|
||||
│ 1 │ 2 │ The quick │
|
||||
│ │ │ fox jumps │
|
||||
│ │ │ over the │
|
||||
│ │ │ lazy brown │
|
||||
│ │ │ dog. │
|
||||
└───────┴──────────┴─────────────┘
|
||||
}
|
||||
do_test 2.9 {
|
||||
catch {db format -align {auto xyz 123} {SELECT * FROM t1}} res
|
||||
set res
|
||||
} {bad column alignment (-align) "xyz": must be auto, bottom, c, center, e, left, middle, n, ne, nw, right, s, se, sw, top, or w}
|
||||
do_test 2.10 {
|
||||
catch {db format -defaultalign xyz {SELECT * FROM t1}} res
|
||||
set res
|
||||
} {bad default alignment (-defaultalign) "xyz": must be auto, bottom, c, center, e, left, middle, n, ne, nw, right, s, se, sw, top, or w}
|
||||
do_test 2.11 {
|
||||
catch {db format -titlealign xyz {SELECT * FROM t1}} res
|
||||
set res
|
||||
} {bad title alignment (-titlealign) "xyz": must be auto, bottom, c, center, e, left, middle, n, ne, nw, right, s, se, sw, top, or w}
|
||||
|
||||
|
||||
do_execsql_test 2.30 {
|
||||
UPDATE t1 SET c='Η γρήγορη αλεπού πηδάει πάνω από το τεμπέλικο καφέ σκυλί';
|
||||
SELECT hex(c) FROM t1;
|
||||
} {CE9720CEB3CF81CEAECEB3CEBFCF81CEB720CEB1CEBBCEB5CF80CEBFCF8D20CF80CEB7CEB4CEACCEB5CEB920CF80CEACCEBDCF8920CEB1CF80CF8C20CF84CEBF20CF84CEB5CEBCCF80CEADCEBBCEB9CEBACEBF20CEBACEB1CF86CEAD20CF83CEBACF85CEBBCEAF}
|
||||
do_test 2.31 {
|
||||
set result "\n[db format -widths {5 -5 18} -wordwrap on \
|
||||
{SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───────┬───────┬────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───────┼───────┼────────────────────┤
|
||||
│ 1 │ 2 │ Η γρήγορη αλεπού │
|
||||
│ │ │ πηδάει πάνω από το │
|
||||
│ │ │ τεμπέλικο καφέ │
|
||||
│ │ │ σκυλί │
|
||||
└───────┴───────┴────────────────────┘
|
||||
}
|
||||
do_test 2.32 {
|
||||
set result "\n[db format -widths {5 5 18} -align {left center center} -wordwrap on \
|
||||
{SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───────┬───────┬────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───────┼───────┼────────────────────┤
|
||||
│ 1 │ 2 │ Η γρήγορη αλεπού │
|
||||
│ │ │ πηδάει πάνω από το │
|
||||
│ │ │ τεμπέλικο καφέ │
|
||||
│ │ │ σκυλί │
|
||||
└───────┴───────┴────────────────────┘
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(1,2,unistr('abc\u001b[1;31m123\u001b[0mxyz'));
|
||||
}
|
||||
do_test 3.1 {
|
||||
set result "\n[db format {SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───┬───┬────────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───┼───┼────────────────────────┤
|
||||
│ 1 │ 2 │ abc^[[1;31m123^[[0mxyz │
|
||||
└───┴───┴────────────────────────┘
|
||||
}
|
||||
do_test 3.2 {
|
||||
set result "\n[db format -esc off {SELECT * FROM t1}]"
|
||||
string map [list \033 X] $result
|
||||
} {
|
||||
┌───┬───┬───────────┐
|
||||
│ a │ b │ c │
|
||||
├───┼───┼───────────┤
|
||||
│ 1 │ 2 │ abcX[1;31m123X[0mxyz │
|
||||
└───┴───┴───────────┘
|
||||
}
|
||||
do_test 3.3 {
|
||||
set result "\n[db format -esc symbol {SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───┬───┬──────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───┼───┼──────────────────────┤
|
||||
│ 1 │ 2 │ abc␛[1;31m123␛[0mxyz │
|
||||
└───┴───┴──────────────────────┘
|
||||
}
|
||||
do_test 3.4 {
|
||||
set result "\n[db format -esc ascii {SELECT * FROM t1}]"
|
||||
} {
|
||||
┌───┬───┬────────────────────────┐
|
||||
│ a │ b │ c │
|
||||
├───┼───┼────────────────────────┤
|
||||
│ 1 │ 2 │ abc^[[1;31m123^[[0mxyz │
|
||||
└───┴───┴────────────────────────┘
|
||||
}
|
||||
do_test 3.5 {
|
||||
catch {db format -esc unk {SELECT * FROM t1}} res
|
||||
set res
|
||||
} {bad control character escape (-esc) "unk": must be ascii, auto, off, or symbol}
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(json('{a:5,b:6}'), jsonb('{c:1,d:2}'), 99);
|
||||
}
|
||||
do_test 4.1 {
|
||||
set result "\n[db format -text sql {SELECT * FROM t1}]"
|
||||
} {
|
||||
┌─────────────────┬───────────────────────┬────┐
|
||||
│ a │ b │ c │
|
||||
├─────────────────┼───────────────────────┼────┤
|
||||
│ '{"a":5,"b":6}' │ x'8c1763133117641332' │ 99 │
|
||||
└─────────────────┴───────────────────────┴────┘
|
||||
}
|
||||
do_test 4.2 {
|
||||
set result "\n[db format -text sql -textjsonb on {SELECT * FROM t1}]"
|
||||
} {
|
||||
┌─────────────────┬────────────────────────┬────┐
|
||||
│ a │ b │ c │
|
||||
├─────────────────┼────────────────────────┼────┤
|
||||
│ '{"a":5,"b":6}' │ jsonb('{"c":1,"d":2}') │ 99 │
|
||||
└─────────────────┴────────────────────────┴────┘
|
||||
}
|
||||
do_test 4.3 {
|
||||
set result "\n[db format -text plain -textjsonb on -wrap 11 \
|
||||
{SELECT a AS json, b AS jsonb, c AS num FROM t1}]"
|
||||
} {
|
||||
┌─────────────┬─────────────┬─────┐
|
||||
│ json │ jsonb │ num │
|
||||
├─────────────┼─────────────┼─────┤
|
||||
│ {"a":5,"b": │ {"c":1,"d": │ 99 │
|
||||
│ 6} │ 2} │ │
|
||||
└─────────────┴─────────────┴─────┘
|
||||
}
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(name, mtime, value);
|
||||
INSERT INTO t1 VALUES
|
||||
('entry-one',1708791504,zeroblob(300)),
|
||||
(unistr('one\u000atwo\u000athree'),1333206973,NULL),
|
||||
('sample-jsonb',1333101221,jsonb('{
|
||||
"alpha":53.11688723,
|
||||
"beta":"qrfWidthPrint(p, p->pOut, -p->u.sLine.mxColWth);",
|
||||
"zeta":[15,null,1333206973,"fd8ffe000104a46494600010101"]}'));
|
||||
}
|
||||
do_test 5.1 {
|
||||
set sql {SELECT name, mtime, datetime(mtime,'unixepoch') AS time,
|
||||
value FROM t1 ORDER BY mtime}
|
||||
set result "\n[db format -style line -screenwidth 60 -blob sql \
|
||||
-text sql -wordwrap off -linelimit 77 $sql]"
|
||||
} {
|
||||
name = 'sample-jsonb'
|
||||
mtime = 1333101221
|
||||
time = '2012-03-30 09:53:41'
|
||||
value = x'cc7c57616c706861b535332e31313638383732334762657461
|
||||
c73071726657696474685072696e7428702c20702d3e704f7574
|
||||
2c202d702d3e752e734c696e652e6d78436f6c577468293b477a
|
||||
657461cb2c23313500a331333333323036393733c71b66643866
|
||||
6665303030313034613436343934363030303130313031'
|
||||
|
||||
name = unistr('one\u000atwo\u000athree')
|
||||
mtime = 1333206973
|
||||
time = '2012-03-31 15:16:13'
|
||||
value =
|
||||
|
||||
name = 'entry-one'
|
||||
mtime = 1708791504
|
||||
time = '2024-02-24 16:18:24'
|
||||
value = x'00000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
000000000000000000000000000000'
|
||||
}
|
||||
do_test 5.2 {
|
||||
set sql {SELECT name, mtime, datetime(mtime,'unixepoch') AS time,
|
||||
value FROM t1 ORDER BY mtime}
|
||||
set result "\n[db format -style line -screenwidth 60 -blob sql \
|
||||
-text plain -esc off -textjsonb yes \
|
||||
-wordwrap yes -linelimit 3 $sql]"
|
||||
} {
|
||||
name = sample-jsonb
|
||||
mtime = 1333101221
|
||||
time = 2012-03-30 09:53:41
|
||||
value = {"alpha":53.11688723,"beta":"qrfWidthPrint(p,
|
||||
p->pOut, -p->u.sLine.mxColWth);","zeta":[15,null,
|
||||
1333206973,"fd8ffe000104a46494600010101"]}
|
||||
|
||||
name = one
|
||||
two
|
||||
three
|
||||
mtime = 1333206973
|
||||
time = 2012-03-31 15:16:13
|
||||
value =
|
||||
|
||||
name = entry-one
|
||||
mtime = 1708791504
|
||||
time = 2024-02-24 16:18:24
|
||||
value = x'00000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000
|
||||
...
|
||||
}
|
||||
do_test 5.3a {
|
||||
set result "\n[db format -style box -widths {0 10 10 14}\
|
||||
-align {left right right center} \
|
||||
-blob sql \
|
||||
-text plain -esc off -textjsonb no \
|
||||
-wordwrap yes -linelimit 2 $sql]"
|
||||
} {
|
||||
┌──────────────┬────────────┬────────────┬────────────────┐
|
||||
│ name │ mtime │ time │ value │
|
||||
├──────────────┼────────────┼────────────┼────────────────┤
|
||||
│ sample-jsonb │ 1333101221 │ 2012-03-30 │ x'cc7c57616c70 │
|
||||
│ │ │ 09:53:41 │ 6861b535332e31 │
|
||||
│ │ │ │ ... │
|
||||
├──────────────┼────────────┼────────────┼────────────────┤
|
||||
│ one │ 1333206973 │ 2012-03-31 │ │
|
||||
│ two │ │ 15:16:13 │ │
|
||||
│ ... │ │ │ │
|
||||
├──────────────┼────────────┼────────────┼────────────────┤
|
||||
│ entry-one │ 1708791504 │ 2024-02-24 │ x'000000000000 │
|
||||
│ │ │ 16:18:24 │ 00000000000000 │
|
||||
│ │ │ │ ... │
|
||||
└──────────────┴────────────┴────────────┴────────────────┘
|
||||
}
|
||||
do_test 5.3b {
|
||||
set result "\n[db format -style table -widths {0 10 10 14}\
|
||||
-align {center right right right} \
|
||||
-blob sql \
|
||||
-text plain -esc off -textjsonb no \
|
||||
-wordwrap yes -linelimit 2 $sql]"
|
||||
} {
|
||||
+--------------+------------+------------+----------------+
|
||||
| name | mtime | time | value |
|
||||
+--------------+------------+------------+----------------+
|
||||
| sample-jsonb | 1333101221 | 2012-03-30 | x'cc7c57616c70 |
|
||||
| | | 09:53:41 | 6861b535332e31 |
|
||||
| | | | ... |
|
||||
+--------------+------------+------------+----------------+
|
||||
| one | 1333206973 | 2012-03-31 | |
|
||||
| two | | 15:16:13 | |
|
||||
| ... | | | |
|
||||
+--------------+------------+------------+----------------+
|
||||
| entry-one | 1708791504 | 2024-02-24 | x'000000000000 |
|
||||
| | | 16:18:24 | 00000000000000 |
|
||||
| | | | ... |
|
||||
+--------------+------------+------------+----------------+
|
||||
}
|
||||
do_test 5.3c {
|
||||
set result "\n[db format -style column -widths {0 10 10 14}\
|
||||
-align {center right right right} \
|
||||
-blob sql \
|
||||
-text plain -esc off -textjsonb no \
|
||||
-wordwrap yes -linelimit 2 $sql]"
|
||||
} {
|
||||
name mtime time value
|
||||
------------ ---------- ---------- --------------
|
||||
sample-jsonb 1333101221 2012-03-30 x'cc7c57616c70
|
||||
09:53:41 6861b535332e31
|
||||
...
|
||||
|
||||
one 1333206973 2012-03-31
|
||||
two 15:16:13
|
||||
...
|
||||
|
||||
entry-one 1708791504 2024-02-24 x'000000000000
|
||||
16:18:24 00000000000000
|
||||
...
|
||||
}
|
||||
do_test 5.4 {
|
||||
db eval {
|
||||
CREATE TABLE t2(a,b,c,d,e);
|
||||
WITH v(x) AS (SELECT 'abcdefghijklmnopqrstuvwxyz')
|
||||
INSERT INTO t2 SELECT x,x,x,x,x FROM v;
|
||||
}
|
||||
set sql {SELECT char(0x61,0xa,0x62,0xa,0x63,0xa,0x64) a,
|
||||
mtime b, mtime c, mtime d, mtime e FROM t1}
|
||||
set result "\n[db format -style box -widths {1 2 3 4 5}\
|
||||
-linelimit 3 -wordwrap off {SELECT *, 'x' AS x FROM t2}]"
|
||||
} {
|
||||
┌────┬────┬─────┬──────┬───────┬───┐
|
||||
│ a │ b │ c │ d │ e │ x │
|
||||
├────┼────┼─────┼──────┼───────┼───┤
|
||||
│ ab │ ab │ abc │ abcd │ abcde │ x │
|
||||
│ cd │ cd │ def │ efgh │ fghij │ │
|
||||
│ ef │ ef │ ghi │ ijkl │ klmno │ │
|
||||
│ .. │ .. │ ... │ ... │ ... │ │
|
||||
└────┴────┴─────┴──────┴───────┴───┘
|
||||
}
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t2 VALUES
|
||||
(1, 2.5, 'three', x'342028666f757229', null);
|
||||
}
|
||||
do_test 6.1a {
|
||||
set result "\n[db format -style list -null NULL \
|
||||
-text tcl -columnsep , \
|
||||
{SELECT * FROM t2}]"
|
||||
} {
|
||||
1,2.5,"three","\064\040\050\146\157\165\162\051",NULL
|
||||
}
|
||||
do_test 6.1b {
|
||||
set result "\n[db format -style list -null NULL \
|
||||
-text tcl -columnsep , -textnull off \
|
||||
{SELECT * FROM t2}]"
|
||||
} {
|
||||
1,2.5,"three","\064\040\050\146\157\165\162\051",NULL
|
||||
}
|
||||
do_test 6.1c {
|
||||
set result "\n[db format -style list -null NULL \
|
||||
-text tcl -columnsep , -textnull auto \
|
||||
{SELECT * FROM t2}]"
|
||||
} {
|
||||
1,2.5,"three","\064\040\050\146\157\165\162\051",NULL
|
||||
}
|
||||
do_test 6.2 {
|
||||
set result "\n[db format -style list -null NULL \
|
||||
-text tcl -columnsep , -textnull yes \
|
||||
{SELECT * FROM t2}]"
|
||||
} {
|
||||
1,2.5,"three","\064\040\050\146\157\165\162\051","NULL"
|
||||
}
|
||||
do_test 6.3 {
|
||||
catch {db format -textnull xyz {SELECT * FROM t2}} res
|
||||
set res
|
||||
} {bad -textnull "xyz": must be auto, yes, no, on, or off}
|
||||
|
||||
do_execsql_test 7.0 {
|
||||
CREATE TABLE t7(a,b);
|
||||
INSERT INTO t7 VALUES('abcdefghijklmnop',
|
||||
'abcぁdefかghiのjklはmnop');
|
||||
}
|
||||
do_test 7.1 {
|
||||
set result "\n[db format -style list -charlimit 13 \
|
||||
{SELECT * FROM t7}]"
|
||||
} {
|
||||
abcdefghijklm...|abcぁdefかghi...
|
||||
}
|
||||
do_test 7.2 {
|
||||
set result "\n[db format -style list -charlimit 14 \
|
||||
{SELECT * FROM t7}]"
|
||||
} {
|
||||
abcdefghijklmn...|abcぁdefかghi...
|
||||
}
|
||||
do_test 7.3 {
|
||||
set result "\n[db format -style list -charlimit 15 \
|
||||
{SELECT * FROM t7}]"
|
||||
} {
|
||||
abcdefghijklmno...|abcぁdefかghiの...
|
||||
}
|
||||
do_test 7.4 {
|
||||
set result "\n[db format -style list -charlimit 16 \
|
||||
{SELECT * FROM t7}]"
|
||||
} {
|
||||
abcdefghijklmnop|abcぁdefかghiのj...
|
||||
}
|
||||
|
||||
do_test 8.0 {
|
||||
set result "\n[db format -style table {
|
||||
WITH c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<10)
|
||||
SELECT 'aaa' AS 'a', format('%.*c',n,'b')||char(9)||'xx' AS x FROM c}]"
|
||||
} {
|
||||
+-----+--------------------+
|
||||
| a | x |
|
||||
+-----+--------------------+
|
||||
| aaa | b xx |
|
||||
| aaa | bb xx |
|
||||
| aaa | bbb xx |
|
||||
| aaa | bbbb xx |
|
||||
| aaa | bbbbb xx |
|
||||
| aaa | bbbbbb xx |
|
||||
| aaa | bbbbbbb xx |
|
||||
| aaa | bbbbbbbb xx |
|
||||
| aaa | bbbbbbbbb xx |
|
||||
| aaa | bbbbbbbbbb xx |
|
||||
+-----+--------------------+
|
||||
}
|
||||
do_test 8.1 {
|
||||
set result "\n[db format -style table {
|
||||
WITH c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<10)
|
||||
SELECT 'aaaa' AS 'a', format('%.*c',n,'b')||char(9)||'xx' AS x FROM c}]"
|
||||
} {
|
||||
+------+--------------------+
|
||||
| a | x |
|
||||
+------+--------------------+
|
||||
| aaaa | b xx |
|
||||
| aaaa | bb xx |
|
||||
| aaaa | bbb xx |
|
||||
| aaaa | bbbb xx |
|
||||
| aaaa | bbbbb xx |
|
||||
| aaaa | bbbbbb xx |
|
||||
| aaaa | bbbbbbb xx |
|
||||
| aaaa | bbbbbbbb xx |
|
||||
| aaaa | bbbbbbbbb xx |
|
||||
| aaaa | bbbbbbbbbb xx |
|
||||
+------+--------------------+
|
||||
}
|
||||
do_test 8.3 {
|
||||
set result "\n[db format -style table -esc off {
|
||||
WITH c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<15)
|
||||
SELECT 'aaa' AS 'a',n, 'xx'||char(n)||'yy' AS xy FROM c
|
||||
WHERE n NOT IN (8,10,13,14)}]"
|
||||
} {
|
||||
+-----+----+------------+
|
||||
| a | n | xy |
|
||||
+-----+----+------------+
|
||||
| aaa | 1 | xx␁yy |
|
||||
| aaa | 2 | xx␂yy |
|
||||
| aaa | 3 | xx␃yy |
|
||||
| aaa | 4 | xx␄yy |
|
||||
| aaa | 5 | xx␅yy |
|
||||
| aaa | 6 | xx␆yy |
|
||||
| aaa | 7 | xx␇yy |
|
||||
| aaa | 9 | xx yy |
|
||||
| aaa | 11 | xx␋yy |
|
||||
| aaa | 12 | xx␌yy |
|
||||
| aaa | 15 | xx␏yy |
|
||||
+-----+----+------------+
|
||||
}
|
||||
do_test 8.4 {
|
||||
set result "\n[db format -style table -esc off {
|
||||
WITH c(n) AS (VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<15)
|
||||
SELECT 'aaa' AS 'a',n, 'xx'||char(n)||'yy'||char(9)||'zz' AS xyz FROM c
|
||||
WHERE n NOT IN (8,10,13,14)}]"
|
||||
} {
|
||||
+-----+----+--------------------+
|
||||
| a | n | xyz |
|
||||
+-----+----+--------------------+
|
||||
| aaa | 1 | xx␁yy zz |
|
||||
| aaa | 2 | xx␂yy zz |
|
||||
| aaa | 3 | xx␃yy zz |
|
||||
| aaa | 4 | xx␄yy zz |
|
||||
| aaa | 5 | xx␅yy zz |
|
||||
| aaa | 6 | xx␆yy zz |
|
||||
| aaa | 7 | xx␇yy zz |
|
||||
| aaa | 9 | xx yy zz |
|
||||
| aaa | 11 | xx␋yy zz |
|
||||
| aaa | 12 | xx␌yy zz |
|
||||
| aaa | 15 | xx␏yy zz |
|
||||
+-----+----+--------------------+
|
||||
}
|
||||
|
||||
do_test 9.1 {
|
||||
db eval {
|
||||
CREATE TABLE t9(x);
|
||||
INSERT INTO t9 VALUES
|
||||
(x'4331323334'),
|
||||
(x'c30431323334'),
|
||||
(x'd3000431323334'),
|
||||
(x'e30000000431323334'),
|
||||
(x'f3000000000000000431323334');
|
||||
}
|
||||
db format -style list -text plain -rowsep , -textjsonb on \
|
||||
{SELECT * FROM t9}
|
||||
} {1234,1234,1234,1234,1234,}
|
||||
do_test 9.2 {
|
||||
db format -style list -text sql -rowsep , -textjsonb on \
|
||||
{SELECT * FROM t9}
|
||||
} {jsonb('1234'),jsonb('1234'),jsonb('1234'),jsonb('1234'),jsonb('1234'),}
|
||||
do_test 9.3 {
|
||||
db format -style json {SELECT * FROM t9 WHERE rowid<0}
|
||||
} {}
|
||||
do_test 9.4 {
|
||||
db format -style jobject {SELECT * FROM t9 WHERE rowid<0}
|
||||
} {}
|
||||
|
||||
do_test 10.1 {
|
||||
db eval {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1(x) VALUES
|
||||
('alice'),
|
||||
('bob'),
|
||||
('cinderella-cinderella'),
|
||||
('daniel'),
|
||||
('emma'),
|
||||
('fred'),
|
||||
('gertrude'),
|
||||
('harold'),
|
||||
('ingrid'),
|
||||
('jake'),
|
||||
('lisa'),
|
||||
('mike'),
|
||||
('nina'),
|
||||
('octavian'),
|
||||
('paula'),
|
||||
('quintus'),
|
||||
('rita'),
|
||||
('sam'),
|
||||
('tammy'),
|
||||
('ulysses'),
|
||||
('violet'),
|
||||
('william'),
|
||||
('xanthippe'),
|
||||
('yates'),
|
||||
('zoe');
|
||||
}
|
||||
set result "\n[db format -style column -title off -screenwidth 41 -splitcolumn on \
|
||||
{SELECT x FROM t1}]"
|
||||
} {
|
||||
alice octavian
|
||||
bob paula
|
||||
cinderella-cinderella quintus
|
||||
daniel rita
|
||||
emma sam
|
||||
fred tammy
|
||||
gertrude ulysses
|
||||
harold violet
|
||||
ingrid william
|
||||
jake xanthippe
|
||||
lisa yates
|
||||
mike zoe
|
||||
nina
|
||||
}
|
||||
do_test 10.2 {
|
||||
set result "\n[db format -style column -title off -screenwidth 42 -splitcolumn on \
|
||||
{SELECT x FROM t1}]"
|
||||
} {
|
||||
alice jake tammy
|
||||
bob lisa ulysses
|
||||
cinderella-cinderella mike violet
|
||||
daniel nina william
|
||||
emma octavian xanthippe
|
||||
fred paula yates
|
||||
gertrude quintus zoe
|
||||
harold rita
|
||||
ingrid sam
|
||||
}
|
||||
do_test 10.3 {
|
||||
set result "\n[db format -style column -title off -screenwidth 51 -splitcolumn on \
|
||||
{SELECT x FROM t1}]"
|
||||
} {
|
||||
alice harold paula william
|
||||
bob ingrid quintus xanthippe
|
||||
cinderella-cinderella jake rita yates
|
||||
daniel lisa sam zoe
|
||||
emma mike tammy
|
||||
fred nina ulysses
|
||||
gertrude octavian violet
|
||||
}
|
||||
do_test 10.4 {
|
||||
set result "\n[db format -style column -title off -screenwidth 61 -splitcolumn on \
|
||||
{SELECT x FROM t1}]"
|
||||
} {
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
}
|
||||
do_test 10.5 {
|
||||
set result "\n[db format -style column -title off -screenwidth 74 -splitcolumn on \
|
||||
{SELECT x FROM t1}]"
|
||||
} {
|
||||
alice emma ingrid nina rita violet zoe
|
||||
bob fred jake octavian sam william
|
||||
cinderella-cinderella gertrude lisa paula tammy xanthippe
|
||||
daniel harold mike quintus ulysses yates
|
||||
}
|
||||
|
||||
db close
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,47 @@
|
||||
# 2025-11-05
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test cases for the Query Result Formatter (QRF)
|
||||
#
|
||||
# These tests are for EXPLAIN and EXPLAIN QUERY PLAN formatting, the
|
||||
# output of which can change when enhancments are made to the query
|
||||
# planner. So expect to have to modify the expected results of these
|
||||
# test cases in the future.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix qrf02
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
}
|
||||
|
||||
set result [db format {EXPLAIN SELECT * FROM t1}]
|
||||
do_test 1.10 {
|
||||
set result
|
||||
} {/*addr opcode p1 p2 p3 p4 p5 comment
|
||||
---- ------------- ---- ---- ---- ------------- -- -------------
|
||||
0 Init */}
|
||||
regsub -all {\d+} $result {N} result2
|
||||
do_test 1.11 {
|
||||
set result2
|
||||
} "/.*\nN Rewind .*\nN Column .*/"
|
||||
|
||||
do_test 1.20 {
|
||||
set result "\n[db format {EXPLAIN QUERY PLAN SELECT * FROM t1}]"
|
||||
} {
|
||||
QUERY PLAN
|
||||
`--SCAN t1
|
||||
}
|
||||
|
||||
finish_test
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
# 2025-11-15
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test cases for the Query Result Formatter (QRF)
|
||||
#
|
||||
# Format narrowing due to nScreenWidth
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix qrf03
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE mlink(
|
||||
mid INTEGER,
|
||||
fid INTEGER,
|
||||
pmid INTEGER,
|
||||
pid INTEGER,
|
||||
fnid INTEGER REFERENCES filename,
|
||||
pfnid INTEGER,
|
||||
mperm INTEGER,
|
||||
isaux BOOLEAN DEFAULT 0
|
||||
);
|
||||
INSERT INTO mlink VALUES(28775,28774,28773,28706,1,0,0,0);
|
||||
INSERT INTO mlink VALUES(28773,28706,28770,28685,1,0,0,0);
|
||||
INSERT INTO mlink VALUES(28770,28736,28769,28695,2,0,0,0);
|
||||
INSERT INTO mlink VALUES(28770,28697,28769,28698,3,0,0,0);
|
||||
INSERT INTO mlink VALUES(28767,28768,28759,28746,4,0,0,0);
|
||||
CREATE TABLE event(
|
||||
type TEXT,
|
||||
mtime DATETIME,
|
||||
objid INTEGER PRIMARY KEY,
|
||||
tagid INTEGER,
|
||||
uid INTEGER REFERENCES user,
|
||||
bgcolor TEXT,
|
||||
euser TEXT,
|
||||
user TEXT,
|
||||
ecomment TEXT,
|
||||
comment TEXT,
|
||||
brief TEXT,
|
||||
omtime DATETIME
|
||||
);
|
||||
INSERT INTO event VALUES('ci',2460994.978048461023,126223,NULL,NULL,NULL,NULL,'drh',NULL,unistr('Data structure improvements on columnar layout. Prep work for getting\u000acolumnar layouts to respond to nScreenWidth.'),NULL,2460994.978048461023);
|
||||
INSERT INTO event VALUES('ci',2460994.836955601816,126218,NULL,NULL,NULL,NULL,'stephan',NULL,'API doc typo fix.',NULL,2460994.836955601816);
|
||||
INSERT INTO event VALUES('ci',2460994.88823369192,126212,NULL,NULL,NULL,NULL,'stephan',NULL,'Move sqlite3-api-cleanup.js into post-js-footer.js to remove the final direct Emscripten dependency from the intermediary build product sqlite3-api.js (the whole library, waiting to be bootstrapped). This is partly in response to [forum:4b7d45433731d2e0|forum post 4b7d45433731d2e0], which demonstrates a potential use case for a standalone sqlite3-api.js. This is a build/doc change, not a functional one.',NULL,2460994.88823369192);
|
||||
INSERT INTO event VALUES('ci',2460994.516081551089,126211,NULL,NULL,NULL,NULL,'drh',NULL,unistr('Improve columnar layout in QRF so that it correctly deals with control\u000acharacters, and especially tabs.'),NULL,2460994.516081551089);
|
||||
INSERT INTO event VALUES('ci',2460994.409343171865,126208,NULL,NULL,NULL,NULL,'drh',NULL,'Make use of the new sqlite3_str_free() interface in the CLI.',NULL,2460994.409343171865);
|
||||
}
|
||||
|
||||
do_test 1.10 {
|
||||
set x "\n[db format -style box -screenwidth 68 \
|
||||
{SELECT * FROM mlink ORDER BY rowid}]"
|
||||
|
||||
} {
|
||||
┌───────┬───────┬───────┬───────┬──────┬───────┬───────┬───────┐
|
||||
│ mid │ fid │ pmid │ pid │ fnid │ pfnid │ mperm │ isaux │
|
||||
├───────┼───────┼───────┼───────┼──────┼───────┼───────┼───────┤
|
||||
│ 28775 │ 28774 │ 28773 │ 28706 │ 1 │ 0 │ 0 │ 0 │
|
||||
│ 28773 │ 28706 │ 28770 │ 28685 │ 1 │ 0 │ 0 │ 0 │
|
||||
│ 28770 │ 28736 │ 28769 │ 28695 │ 2 │ 0 │ 0 │ 0 │
|
||||
│ 28770 │ 28697 │ 28769 │ 28698 │ 3 │ 0 │ 0 │ 0 │
|
||||
│ 28767 │ 28768 │ 28759 │ 28746 │ 4 │ 0 │ 0 │ 0 │
|
||||
└───────┴───────┴───────┴───────┴──────┴───────┴───────┴───────┘
|
||||
}
|
||||
do_test 1.11 {
|
||||
set x "\n[db format -style box -screenwidth 52 \
|
||||
{SELECT * FROM mlink ORDER BY rowid}]"
|
||||
|
||||
} {
|
||||
┌─────┬─────┬─────┬─────┬────┬─────┬─────┬─────┐
|
||||
│ mid │ fid │pmid │ pid │fnid│pfnid│mperm│isaux│
|
||||
├─────┼─────┼─────┼─────┼────┼─────┼─────┼─────┤
|
||||
│28775│28774│28773│28706│1 │0 │0 │0 │
|
||||
│28773│28706│28770│28685│1 │0 │0 │0 │
|
||||
│28770│28736│28769│28695│2 │0 │0 │0 │
|
||||
│28770│28697│28769│28698│3 │0 │0 │0 │
|
||||
│28767│28768│28759│28746│4 │0 │0 │0 │
|
||||
└─────┴─────┴─────┴─────┴────┴─────┴─────┴─────┘
|
||||
}
|
||||
|
||||
do_test 1.20 {
|
||||
set x "\n[db format -style table -screenwidth 68 \
|
||||
{SELECT * FROM mlink ORDER BY rowid}]"
|
||||
|
||||
} {
|
||||
+-------+-------+-------+-------+------+-------+-------+-------+
|
||||
| mid | fid | pmid | pid | fnid | pfnid | mperm | isaux |
|
||||
+-------+-------+-------+-------+------+-------+-------+-------+
|
||||
| 28775 | 28774 | 28773 | 28706 | 1 | 0 | 0 | 0 |
|
||||
| 28773 | 28706 | 28770 | 28685 | 1 | 0 | 0 | 0 |
|
||||
| 28770 | 28736 | 28769 | 28695 | 2 | 0 | 0 | 0 |
|
||||
| 28770 | 28697 | 28769 | 28698 | 3 | 0 | 0 | 0 |
|
||||
| 28767 | 28768 | 28759 | 28746 | 4 | 0 | 0 | 0 |
|
||||
+-------+-------+-------+-------+------+-------+-------+-------+
|
||||
}
|
||||
do_test 1.21 {
|
||||
set x "\n[db format -style table -screenwidth 52 \
|
||||
{SELECT * FROM mlink ORDER BY rowid}]"
|
||||
|
||||
} {
|
||||
+-----+-----+-----+-----+----+-----+-----+-----+
|
||||
| mid | fid |pmid | pid |fnid|pfnid|mperm|isaux|
|
||||
+-----+-----+-----+-----+----+-----+-----+-----+
|
||||
|28775|28774|28773|28706|1 |0 |0 |0 |
|
||||
|28773|28706|28770|28685|1 |0 |0 |0 |
|
||||
|28770|28736|28769|28695|2 |0 |0 |0 |
|
||||
|28770|28697|28769|28698|3 |0 |0 |0 |
|
||||
|28767|28768|28759|28746|4 |0 |0 |0 |
|
||||
+-----+-----+-----+-----+----+-----+-----+-----+
|
||||
}
|
||||
|
||||
do_test 1.30 {
|
||||
set x "\n[db format -style markdown -screenwidth 68 \
|
||||
{SELECT * FROM mlink ORDER BY rowid}]"
|
||||
|
||||
} {
|
||||
| mid | fid | pmid | pid | fnid | pfnid | mperm | isaux |
|
||||
|-------|-------|-------|-------|------|-------|-------|-------|
|
||||
| 28775 | 28774 | 28773 | 28706 | 1 | 0 | 0 | 0 |
|
||||
| 28773 | 28706 | 28770 | 28685 | 1 | 0 | 0 | 0 |
|
||||
| 28770 | 28736 | 28769 | 28695 | 2 | 0 | 0 | 0 |
|
||||
| 28770 | 28697 | 28769 | 28698 | 3 | 0 | 0 | 0 |
|
||||
| 28767 | 28768 | 28759 | 28746 | 4 | 0 | 0 | 0 |
|
||||
}
|
||||
do_test 1.31 {
|
||||
set x "\n[db format -style markdown -screenwidth 52 \
|
||||
{SELECT * FROM mlink ORDER BY rowid}]"
|
||||
|
||||
} {
|
||||
| mid | fid |pmid | pid |fnid|pfnid|mperm|isaux|
|
||||
|-----|-----|-----|-----|----|-----|-----|-----|
|
||||
|28775|28774|28773|28706|1 |0 |0 |0 |
|
||||
|28773|28706|28770|28685|1 |0 |0 |0 |
|
||||
|28770|28736|28769|28695|2 |0 |0 |0 |
|
||||
|28770|28697|28769|28698|3 |0 |0 |0 |
|
||||
|28767|28768|28759|28746|4 |0 |0 |0 |
|
||||
}
|
||||
|
||||
do_test 1.40 {
|
||||
set x "\n[db format -style column -screenwidth 68 \
|
||||
{SELECT * FROM mlink ORDER BY rowid}]"
|
||||
|
||||
} {
|
||||
mid fid pmid pid fnid pfnid mperm isaux
|
||||
----- ----- ----- ----- ---- ----- ----- -----
|
||||
28775 28774 28773 28706 1 0 0 0
|
||||
28773 28706 28770 28685 1 0 0 0
|
||||
28770 28736 28769 28695 2 0 0 0
|
||||
28770 28697 28769 28698 3 0 0 0
|
||||
28767 28768 28759 28746 4 0 0 0
|
||||
}
|
||||
do_test 1.41 {
|
||||
set x "\n[db format -style column -screenwidth 52 \
|
||||
{SELECT * FROM mlink ORDER BY rowid}]"
|
||||
|
||||
} {
|
||||
mid fid pmid pid fnid pfnid mperm isaux
|
||||
----- ----- ----- ----- ---- ----- ----- -----
|
||||
28775 28774 28773 28706 1 0 0 0
|
||||
28773 28706 28770 28685 1 0 0 0
|
||||
28770 28736 28769 28695 2 0 0 0
|
||||
28770 28697 28769 28698 3 0 0 0
|
||||
28767 28768 28759 28746 4 0 0 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
+750
@@ -0,0 +1,750 @@
|
||||
# 2025-11-23
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test cases for the Query Result Formatter (QRF), and especially
|
||||
# the bSplitColumn feature.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix qrf01
|
||||
|
||||
# The expected output from test 1.1. The "do_test" procedure normally
|
||||
# ignores differences in whitespace, but whitespace is important for
|
||||
# this test, so we have to do the comparison ourselves.
|
||||
#
|
||||
set expected {
|
||||
<---- 22 ---->
|
||||
alice
|
||||
bob
|
||||
cinderella-cinderella
|
||||
daniel
|
||||
emma
|
||||
fred
|
||||
gertrude
|
||||
harold
|
||||
ingrid
|
||||
jake
|
||||
lisa
|
||||
mike
|
||||
nina
|
||||
octavian
|
||||
paula
|
||||
quintus
|
||||
rita
|
||||
sam
|
||||
tammy
|
||||
ulysses
|
||||
violet
|
||||
william
|
||||
xanthippe
|
||||
yates
|
||||
zoe
|
||||
<---- 23 ---->
|
||||
alice
|
||||
bob
|
||||
cinderella-cinderella
|
||||
daniel
|
||||
emma
|
||||
fred
|
||||
gertrude
|
||||
harold
|
||||
ingrid
|
||||
jake
|
||||
lisa
|
||||
mike
|
||||
nina
|
||||
octavian
|
||||
paula
|
||||
quintus
|
||||
rita
|
||||
sam
|
||||
tammy
|
||||
ulysses
|
||||
violet
|
||||
william
|
||||
xanthippe
|
||||
yates
|
||||
zoe
|
||||
<---- 24 ---->
|
||||
alice
|
||||
bob
|
||||
cinderella-cinderella
|
||||
daniel
|
||||
emma
|
||||
fred
|
||||
gertrude
|
||||
harold
|
||||
ingrid
|
||||
jake
|
||||
lisa
|
||||
mike
|
||||
nina
|
||||
octavian
|
||||
paula
|
||||
quintus
|
||||
rita
|
||||
sam
|
||||
tammy
|
||||
ulysses
|
||||
violet
|
||||
william
|
||||
xanthippe
|
||||
yates
|
||||
zoe
|
||||
<---- 25 ---->
|
||||
alice
|
||||
bob
|
||||
cinderella-cinderella
|
||||
daniel
|
||||
emma
|
||||
fred
|
||||
gertrude
|
||||
harold
|
||||
ingrid
|
||||
jake
|
||||
lisa
|
||||
mike
|
||||
nina
|
||||
octavian
|
||||
paula
|
||||
quintus
|
||||
rita
|
||||
sam
|
||||
tammy
|
||||
ulysses
|
||||
violet
|
||||
william
|
||||
xanthippe
|
||||
yates
|
||||
zoe
|
||||
<---- 26 ---->
|
||||
alice
|
||||
bob
|
||||
cinderella-cinderella
|
||||
daniel
|
||||
emma
|
||||
fred
|
||||
gertrude
|
||||
harold
|
||||
ingrid
|
||||
jake
|
||||
lisa
|
||||
mike
|
||||
nina
|
||||
octavian
|
||||
paula
|
||||
quintus
|
||||
rita
|
||||
sam
|
||||
tammy
|
||||
ulysses
|
||||
violet
|
||||
william
|
||||
xanthippe
|
||||
yates
|
||||
zoe
|
||||
<---- 27 ---->
|
||||
alice
|
||||
bob
|
||||
cinderella-cinderella
|
||||
daniel
|
||||
emma
|
||||
fred
|
||||
gertrude
|
||||
harold
|
||||
ingrid
|
||||
jake
|
||||
lisa
|
||||
mike
|
||||
nina
|
||||
octavian
|
||||
paula
|
||||
quintus
|
||||
rita
|
||||
sam
|
||||
tammy
|
||||
ulysses
|
||||
violet
|
||||
william
|
||||
xanthippe
|
||||
yates
|
||||
zoe
|
||||
<---- 28 ---->
|
||||
alice
|
||||
bob
|
||||
cinderella-cinderella
|
||||
daniel
|
||||
emma
|
||||
fred
|
||||
gertrude
|
||||
harold
|
||||
ingrid
|
||||
jake
|
||||
lisa
|
||||
mike
|
||||
nina
|
||||
octavian
|
||||
paula
|
||||
quintus
|
||||
rita
|
||||
sam
|
||||
tammy
|
||||
ulysses
|
||||
violet
|
||||
william
|
||||
xanthippe
|
||||
yates
|
||||
zoe
|
||||
<---- 29 ---->
|
||||
alice
|
||||
bob
|
||||
cinderella-cinderella
|
||||
daniel
|
||||
emma
|
||||
fred
|
||||
gertrude
|
||||
harold
|
||||
ingrid
|
||||
jake
|
||||
lisa
|
||||
mike
|
||||
nina
|
||||
octavian
|
||||
paula
|
||||
quintus
|
||||
rita
|
||||
sam
|
||||
tammy
|
||||
ulysses
|
||||
violet
|
||||
william
|
||||
xanthippe
|
||||
yates
|
||||
zoe
|
||||
<---- 30 ---->
|
||||
alice
|
||||
bob
|
||||
cinderella-cinderella
|
||||
daniel
|
||||
emma
|
||||
fred
|
||||
gertrude
|
||||
harold
|
||||
ingrid
|
||||
jake
|
||||
lisa
|
||||
mike
|
||||
nina
|
||||
octavian
|
||||
paula
|
||||
quintus
|
||||
rita
|
||||
sam
|
||||
tammy
|
||||
ulysses
|
||||
violet
|
||||
william
|
||||
xanthippe
|
||||
yates
|
||||
zoe
|
||||
<---- 31 ---->
|
||||
alice
|
||||
bob
|
||||
cinderella-cinderella
|
||||
daniel
|
||||
emma
|
||||
fred
|
||||
gertrude
|
||||
harold
|
||||
ingrid
|
||||
jake
|
||||
lisa
|
||||
mike
|
||||
nina
|
||||
octavian
|
||||
paula
|
||||
quintus
|
||||
rita
|
||||
sam
|
||||
tammy
|
||||
ulysses
|
||||
violet
|
||||
william
|
||||
xanthippe
|
||||
yates
|
||||
zoe
|
||||
<---- 32 ---->
|
||||
alice octavian
|
||||
bob paula
|
||||
cinderella-cinderella quintus
|
||||
daniel rita
|
||||
emma sam
|
||||
fred tammy
|
||||
gertrude ulysses
|
||||
harold violet
|
||||
ingrid william
|
||||
jake xanthippe
|
||||
lisa yates
|
||||
mike zoe
|
||||
nina
|
||||
<---- 33 ---->
|
||||
alice octavian
|
||||
bob paula
|
||||
cinderella-cinderella quintus
|
||||
daniel rita
|
||||
emma sam
|
||||
fred tammy
|
||||
gertrude ulysses
|
||||
harold violet
|
||||
ingrid william
|
||||
jake xanthippe
|
||||
lisa yates
|
||||
mike zoe
|
||||
nina
|
||||
<---- 34 ---->
|
||||
alice octavian
|
||||
bob paula
|
||||
cinderella-cinderella quintus
|
||||
daniel rita
|
||||
emma sam
|
||||
fred tammy
|
||||
gertrude ulysses
|
||||
harold violet
|
||||
ingrid william
|
||||
jake xanthippe
|
||||
lisa yates
|
||||
mike zoe
|
||||
nina
|
||||
<---- 35 ---->
|
||||
alice octavian
|
||||
bob paula
|
||||
cinderella-cinderella quintus
|
||||
daniel rita
|
||||
emma sam
|
||||
fred tammy
|
||||
gertrude ulysses
|
||||
harold violet
|
||||
ingrid william
|
||||
jake xanthippe
|
||||
lisa yates
|
||||
mike zoe
|
||||
nina
|
||||
<---- 36 ---->
|
||||
alice octavian
|
||||
bob paula
|
||||
cinderella-cinderella quintus
|
||||
daniel rita
|
||||
emma sam
|
||||
fred tammy
|
||||
gertrude ulysses
|
||||
harold violet
|
||||
ingrid william
|
||||
jake xanthippe
|
||||
lisa yates
|
||||
mike zoe
|
||||
nina
|
||||
<---- 37 ---->
|
||||
alice octavian
|
||||
bob paula
|
||||
cinderella-cinderella quintus
|
||||
daniel rita
|
||||
emma sam
|
||||
fred tammy
|
||||
gertrude ulysses
|
||||
harold violet
|
||||
ingrid william
|
||||
jake xanthippe
|
||||
lisa yates
|
||||
mike zoe
|
||||
nina
|
||||
<---- 38 ---->
|
||||
alice octavian
|
||||
bob paula
|
||||
cinderella-cinderella quintus
|
||||
daniel rita
|
||||
emma sam
|
||||
fred tammy
|
||||
gertrude ulysses
|
||||
harold violet
|
||||
ingrid william
|
||||
jake xanthippe
|
||||
lisa yates
|
||||
mike zoe
|
||||
nina
|
||||
<---- 39 ---->
|
||||
alice octavian
|
||||
bob paula
|
||||
cinderella-cinderella quintus
|
||||
daniel rita
|
||||
emma sam
|
||||
fred tammy
|
||||
gertrude ulysses
|
||||
harold violet
|
||||
ingrid william
|
||||
jake xanthippe
|
||||
lisa yates
|
||||
mike zoe
|
||||
nina
|
||||
<---- 40 ---->
|
||||
alice octavian
|
||||
bob paula
|
||||
cinderella-cinderella quintus
|
||||
daniel rita
|
||||
emma sam
|
||||
fred tammy
|
||||
gertrude ulysses
|
||||
harold violet
|
||||
ingrid william
|
||||
jake xanthippe
|
||||
lisa yates
|
||||
mike zoe
|
||||
nina
|
||||
<---- 41 ---->
|
||||
alice octavian
|
||||
bob paula
|
||||
cinderella-cinderella quintus
|
||||
daniel rita
|
||||
emma sam
|
||||
fred tammy
|
||||
gertrude ulysses
|
||||
harold violet
|
||||
ingrid william
|
||||
jake xanthippe
|
||||
lisa yates
|
||||
mike zoe
|
||||
nina
|
||||
<---- 42 ---->
|
||||
alice jake tammy
|
||||
bob lisa ulysses
|
||||
cinderella-cinderella mike violet
|
||||
daniel nina william
|
||||
emma octavian xanthippe
|
||||
fred paula yates
|
||||
gertrude quintus zoe
|
||||
harold rita
|
||||
ingrid sam
|
||||
<---- 43 ---->
|
||||
alice jake tammy
|
||||
bob lisa ulysses
|
||||
cinderella-cinderella mike violet
|
||||
daniel nina william
|
||||
emma octavian xanthippe
|
||||
fred paula yates
|
||||
gertrude quintus zoe
|
||||
harold rita
|
||||
ingrid sam
|
||||
<---- 44 ---->
|
||||
alice jake tammy
|
||||
bob lisa ulysses
|
||||
cinderella-cinderella mike violet
|
||||
daniel nina william
|
||||
emma octavian xanthippe
|
||||
fred paula yates
|
||||
gertrude quintus zoe
|
||||
harold rita
|
||||
ingrid sam
|
||||
<---- 45 ---->
|
||||
alice jake tammy
|
||||
bob lisa ulysses
|
||||
cinderella-cinderella mike violet
|
||||
daniel nina william
|
||||
emma octavian xanthippe
|
||||
fred paula yates
|
||||
gertrude quintus zoe
|
||||
harold rita
|
||||
ingrid sam
|
||||
<---- 46 ---->
|
||||
alice jake tammy
|
||||
bob lisa ulysses
|
||||
cinderella-cinderella mike violet
|
||||
daniel nina william
|
||||
emma octavian xanthippe
|
||||
fred paula yates
|
||||
gertrude quintus zoe
|
||||
harold rita
|
||||
ingrid sam
|
||||
<---- 47 ---->
|
||||
alice jake tammy
|
||||
bob lisa ulysses
|
||||
cinderella-cinderella mike violet
|
||||
daniel nina william
|
||||
emma octavian xanthippe
|
||||
fred paula yates
|
||||
gertrude quintus zoe
|
||||
harold rita
|
||||
ingrid sam
|
||||
<---- 48 ---->
|
||||
alice jake tammy
|
||||
bob lisa ulysses
|
||||
cinderella-cinderella mike violet
|
||||
daniel nina william
|
||||
emma octavian xanthippe
|
||||
fred paula yates
|
||||
gertrude quintus zoe
|
||||
harold rita
|
||||
ingrid sam
|
||||
<---- 49 ---->
|
||||
alice jake tammy
|
||||
bob lisa ulysses
|
||||
cinderella-cinderella mike violet
|
||||
daniel nina william
|
||||
emma octavian xanthippe
|
||||
fred paula yates
|
||||
gertrude quintus zoe
|
||||
harold rita
|
||||
ingrid sam
|
||||
<---- 50 ---->
|
||||
alice jake tammy
|
||||
bob lisa ulysses
|
||||
cinderella-cinderella mike violet
|
||||
daniel nina william
|
||||
emma octavian xanthippe
|
||||
fred paula yates
|
||||
gertrude quintus zoe
|
||||
harold rita
|
||||
ingrid sam
|
||||
<---- 51 ---->
|
||||
alice harold paula william
|
||||
bob ingrid quintus xanthippe
|
||||
cinderella-cinderella jake rita yates
|
||||
daniel lisa sam zoe
|
||||
emma mike tammy
|
||||
fred nina ulysses
|
||||
gertrude octavian violet
|
||||
<---- 52 ---->
|
||||
alice harold paula william
|
||||
bob ingrid quintus xanthippe
|
||||
cinderella-cinderella jake rita yates
|
||||
daniel lisa sam zoe
|
||||
emma mike tammy
|
||||
fred nina ulysses
|
||||
gertrude octavian violet
|
||||
<---- 53 ---->
|
||||
alice harold paula william
|
||||
bob ingrid quintus xanthippe
|
||||
cinderella-cinderella jake rita yates
|
||||
daniel lisa sam zoe
|
||||
emma mike tammy
|
||||
fred nina ulysses
|
||||
gertrude octavian violet
|
||||
<---- 54 ---->
|
||||
alice harold paula william
|
||||
bob ingrid quintus xanthippe
|
||||
cinderella-cinderella jake rita yates
|
||||
daniel lisa sam zoe
|
||||
emma mike tammy
|
||||
fred nina ulysses
|
||||
gertrude octavian violet
|
||||
<---- 55 ---->
|
||||
alice harold paula william
|
||||
bob ingrid quintus xanthippe
|
||||
cinderella-cinderella jake rita yates
|
||||
daniel lisa sam zoe
|
||||
emma mike tammy
|
||||
fred nina ulysses
|
||||
gertrude octavian violet
|
||||
<---- 56 ---->
|
||||
alice harold paula william
|
||||
bob ingrid quintus xanthippe
|
||||
cinderella-cinderella jake rita yates
|
||||
daniel lisa sam zoe
|
||||
emma mike tammy
|
||||
fred nina ulysses
|
||||
gertrude octavian violet
|
||||
<---- 57 ---->
|
||||
alice harold paula william
|
||||
bob ingrid quintus xanthippe
|
||||
cinderella-cinderella jake rita yates
|
||||
daniel lisa sam zoe
|
||||
emma mike tammy
|
||||
fred nina ulysses
|
||||
gertrude octavian violet
|
||||
<---- 58 ---->
|
||||
alice harold paula william
|
||||
bob ingrid quintus xanthippe
|
||||
cinderella-cinderella jake rita yates
|
||||
daniel lisa sam zoe
|
||||
emma mike tammy
|
||||
fred nina ulysses
|
||||
gertrude octavian violet
|
||||
<---- 59 ---->
|
||||
alice harold paula william
|
||||
bob ingrid quintus xanthippe
|
||||
cinderella-cinderella jake rita yates
|
||||
daniel lisa sam zoe
|
||||
emma mike tammy
|
||||
fred nina ulysses
|
||||
gertrude octavian violet
|
||||
<---- 60 ---->
|
||||
alice harold paula william
|
||||
bob ingrid quintus xanthippe
|
||||
cinderella-cinderella jake rita yates
|
||||
daniel lisa sam zoe
|
||||
emma mike tammy
|
||||
fred nina ulysses
|
||||
gertrude octavian violet
|
||||
<---- 61 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 62 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 63 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 64 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 65 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 66 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 67 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 68 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 69 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 70 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 71 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 72 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 73 ---->
|
||||
alice fred lisa quintus violet
|
||||
bob gertrude mike rita william
|
||||
cinderella-cinderella harold nina sam xanthippe
|
||||
daniel ingrid octavian tammy yates
|
||||
emma jake paula ulysses zoe
|
||||
<---- 74 ---->
|
||||
alice emma ingrid nina rita violet zoe
|
||||
bob fred jake octavian sam william
|
||||
cinderella-cinderella gertrude lisa paula tammy xanthippe
|
||||
daniel harold mike quintus ulysses yates
|
||||
<---- 75 ---->
|
||||
alice emma ingrid nina rita violet zoe
|
||||
bob fred jake octavian sam william
|
||||
cinderella-cinderella gertrude lisa paula tammy xanthippe
|
||||
daniel harold mike quintus ulysses yates
|
||||
<---- 76 ---->
|
||||
alice emma ingrid nina rita violet zoe
|
||||
bob fred jake octavian sam william
|
||||
cinderella-cinderella gertrude lisa paula tammy xanthippe
|
||||
daniel harold mike quintus ulysses yates
|
||||
<---- 77 ---->
|
||||
alice emma ingrid nina rita violet zoe
|
||||
bob fred jake octavian sam william
|
||||
cinderella-cinderella gertrude lisa paula tammy xanthippe
|
||||
daniel harold mike quintus ulysses yates
|
||||
<---- 78 ---->
|
||||
alice emma ingrid nina rita violet zoe
|
||||
bob fred jake octavian sam william
|
||||
cinderella-cinderella gertrude lisa paula tammy xanthippe
|
||||
daniel harold mike quintus ulysses yates
|
||||
<---- 79 ---->
|
||||
alice emma ingrid nina rita violet zoe
|
||||
bob fred jake octavian sam william
|
||||
cinderella-cinderella gertrude lisa paula tammy xanthippe
|
||||
daniel harold mike quintus ulysses yates
|
||||
<---- 80 ---->
|
||||
alice emma ingrid nina rita violet zoe
|
||||
bob fred jake octavian sam william
|
||||
cinderella-cinderella gertrude lisa paula tammy xanthippe
|
||||
daniel harold mike quintus ulysses yates
|
||||
}
|
||||
|
||||
do_test 1.0 {
|
||||
db eval {
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1(x) VALUES
|
||||
('alice'),
|
||||
('bob'),
|
||||
('cinderella-cinderella'),
|
||||
('daniel'),
|
||||
('emma'),
|
||||
('fred'),
|
||||
('gertrude'),
|
||||
('harold'),
|
||||
('ingrid'),
|
||||
('jake'),
|
||||
('lisa'),
|
||||
('mike'),
|
||||
('nina'),
|
||||
('octavian'),
|
||||
('paula'),
|
||||
('quintus'),
|
||||
('rita'),
|
||||
('sam'),
|
||||
('tammy'),
|
||||
('ulysses'),
|
||||
('violet'),
|
||||
('william'),
|
||||
('xanthippe'),
|
||||
('yates'),
|
||||
('zoe');
|
||||
}
|
||||
set res \n
|
||||
for {set i 22} {$i<=80} {incr i} {
|
||||
set sp [expr {$i-13}]
|
||||
append res [format "<----%*s%3d%*s---->\n" \
|
||||
[expr {$sp/2}] {} $i [expr {$sp-$sp/2}] {}]
|
||||
append res [db format -style column -title off \
|
||||
-screenwidth $i -splitcolumn on \
|
||||
{SELECT x FROM t1 ORDER BY x ASC}]
|
||||
}
|
||||
expr {$res eq $::expected}
|
||||
} {1}
|
||||
+1
-1
@@ -42,7 +42,7 @@ proc compare_dbs {db1 db2} {
|
||||
|
||||
proc recover_with_opts {opts} {
|
||||
set cmd ".recover $opts"
|
||||
set fd [open [list |$::CLI test.db $cmd]]
|
||||
set fd [open [list |$::CLI -noinit test.db $cmd]]
|
||||
fconfigure $fd -translation binary
|
||||
set sql [read $fd]
|
||||
close $fd
|
||||
|
||||
+90
-73
@@ -48,7 +48,7 @@ do_test shell1-1.1.1b {
|
||||
} {1 1}
|
||||
# error on extra options
|
||||
do_test shell1-1.1.2 {
|
||||
catchcmd "test.db \"select+3\" \"select+4\"" ""
|
||||
catchcmd "-compat 20250101 test.db \"select+3\" \"select+4\"" ""
|
||||
} {0 {3
|
||||
4}}
|
||||
# error on extra options
|
||||
@@ -182,7 +182,7 @@ do_test shell1-1.17.1 {
|
||||
catchcmd {-- --db "CREATE TABLE T(c1);"}
|
||||
} {0 {}}
|
||||
do_test shell1-1.17.2 {
|
||||
catchcmd {-- --db "SELECT name from sqlite_schema;"}
|
||||
catchcmd {-compat 20250101 -- --db "SELECT name from sqlite_schema;"}
|
||||
} {0 T}
|
||||
forcedelete ./--db
|
||||
|
||||
@@ -216,10 +216,14 @@ do_test shell1-2.2.4 {
|
||||
} {0 {}}
|
||||
do_test shell1-2.2.5 {
|
||||
catchcmd "test.db" ".mode \"insert FOO"
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
|
||||
} {1 {line 1: .mode "insert FOO
|
||||
line 1: ^--- unknown mode
|
||||
line 1: Use ".help .mode" for more info}}
|
||||
do_test shell1-2.2.6 {
|
||||
catchcmd "test.db" ".mode \'insert FOO"
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
|
||||
} {1 {line 1: .mode 'insert FOO
|
||||
line 1: ^--- unknown mode
|
||||
line 1: Use ".help .mode" for more info}}
|
||||
|
||||
# check multiple tokens, and quoted tokens
|
||||
do_test shell1-2.3.1 {
|
||||
@@ -247,7 +251,9 @@ do_test shell1-2.3.7 {
|
||||
# check quoted args are unquoted
|
||||
do_test shell1-2.4.1 {
|
||||
catchcmd "test.db" ".mode FOO"
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
|
||||
} {1 {line 1: .mode FOO
|
||||
line 1: ^--- unknown mode
|
||||
line 1: Use ".help .mode" for more info}}
|
||||
do_test shell1-2.4.2 {
|
||||
catchcmd "test.db" ".mode csv"
|
||||
} {0 {}}
|
||||
@@ -364,7 +370,6 @@ do_test shell1-3.7.4 {
|
||||
catchcmd "test.db" ".explain OFF BAD"
|
||||
} {0 {}}
|
||||
|
||||
|
||||
# .header(s) ON|OFF Turn display of headers on or off
|
||||
do_test shell1-3.9.1 {
|
||||
catchcmd "test.db" ".header"
|
||||
@@ -400,7 +405,7 @@ do_test shell1-3.10.1 {
|
||||
# look for a few of the possible help commands
|
||||
list [regexp {.help} $res] \
|
||||
[regexp {.quit} $res] \
|
||||
[regexp {.show} $res]
|
||||
[regexp {.mode} $res]
|
||||
} {1 1 1}
|
||||
do_test shell1-3.10.2 {
|
||||
# we allow .help to take extra args (it is help after all)
|
||||
@@ -408,7 +413,7 @@ do_test shell1-3.10.2 {
|
||||
# look for a few of the possible help commands
|
||||
list [regexp {.help} $res] \
|
||||
[regexp {.quit} $res] \
|
||||
[regexp {.show} $res]
|
||||
[regexp {.mode} $res]
|
||||
} {1 1 1}
|
||||
|
||||
# .import FILE TABLE Import data from FILE into TABLE
|
||||
@@ -421,7 +426,8 @@ do_test shell1-3.11.2 {
|
||||
do_test shell1-3.11.3 {
|
||||
# too many arguments
|
||||
catchcmd "test.db" ".import FOO BAR BAD"
|
||||
} {/1 .ERROR: extra argument: "BAD".*./}
|
||||
} {1 {line 1: .import FOO BAR BAD
|
||||
line 1: ^--- unknown argument}}
|
||||
|
||||
# .indexes ?TABLE? Show names of all indexes
|
||||
# If TABLE specified, only show indexes for tables
|
||||
@@ -451,11 +457,13 @@ do_test shell1-3.12.3 {
|
||||
# tabs Tab-separated values
|
||||
# tcl TCL list elements
|
||||
do_test shell1-3.13.1 {
|
||||
catchcmd "test.db" ".mode"
|
||||
} {0 {current output mode: list --escape ascii}}
|
||||
catchcmd "test.db" ".mode batch\n.mode"
|
||||
} {0 {current output mode: list}}
|
||||
do_test shell1-3.13.2 {
|
||||
catchcmd "test.db" ".mode FOO"
|
||||
} {1 {Error: mode should be one of: ascii box column csv html insert json line list markdown qbox quote table tabs tcl}}
|
||||
} {1 {line 1: .mode FOO
|
||||
line 1: ^--- unknown mode
|
||||
line 1: Use ".help .mode" for more info}}
|
||||
do_test shell1-3.13.3 {
|
||||
catchcmd "test.db" ".mode csv"
|
||||
} {0 {}}
|
||||
@@ -503,7 +511,7 @@ do_test shell1-3.15.1 {
|
||||
.print x"
|
||||
} {0 x}
|
||||
do_test shell1-3.15.2 {
|
||||
catchcmd "test.db" ".output FOO
|
||||
catchcmd "test.db" ".mode batch\n.output FOO
|
||||
.print x
|
||||
.output
|
||||
SELECT readfile('FOO');"
|
||||
@@ -512,17 +520,8 @@ SELECT readfile('FOO');"
|
||||
do_test shell1-3.15.3 {
|
||||
# too many arguments
|
||||
catchcmd "test.db" ".output FOO BAD"
|
||||
} {1 {ERROR: extra parameter: "BAD". Usage:
|
||||
.output ?FILE? Send output to FILE or stdout if FILE is omitted
|
||||
If FILE begins with '|' then open it as a pipe.
|
||||
If FILE is 'off' then output is disabled.
|
||||
Options:
|
||||
--bom Prefix output with a UTF8 byte-order mark
|
||||
-e Send output to the system text editor
|
||||
--plain Use text/plain for -w option
|
||||
-w Send output to a web browser
|
||||
-x Send output as CSV to a spreadsheet
|
||||
child process exited abnormally}}
|
||||
} {1 {line 1: .output FOO BAD
|
||||
line 1: ^--- surplus argument}}
|
||||
|
||||
# .output stdout Send output to the screen
|
||||
do_test shell1-3.16.1 {
|
||||
@@ -531,17 +530,8 @@ do_test shell1-3.16.1 {
|
||||
do_test shell1-3.16.2 {
|
||||
# too many arguments
|
||||
catchcmd "test.db" ".output stdout BAD"
|
||||
} {1 {ERROR: extra parameter: "BAD". Usage:
|
||||
.output ?FILE? Send output to FILE or stdout if FILE is omitted
|
||||
If FILE begins with '|' then open it as a pipe.
|
||||
If FILE is 'off' then output is disabled.
|
||||
Options:
|
||||
--bom Prefix output with a UTF8 byte-order mark
|
||||
-e Send output to the system text editor
|
||||
--plain Use text/plain for -w option
|
||||
-w Send output to a web browser
|
||||
-x Send output as CSV to a spreadsheet
|
||||
child process exited abnormally}}
|
||||
} {1 {line 1: .output stdout BAD
|
||||
line 1: ^--- surplus argument}}
|
||||
|
||||
# .prompt MAIN CONTINUE Replace the standard prompts
|
||||
do_test shell1-3.17.1 {
|
||||
@@ -643,7 +633,7 @@ do_test shell1-3.22.4 {
|
||||
|
||||
# .show Show the current values for various settings
|
||||
do_test shell1-3.23.1 {
|
||||
set res [catchcmd "test.db" ".show"]
|
||||
set res [catchcmd "test.db" ".mode batch\n.show"]
|
||||
list [regexp {echo:} $res] \
|
||||
[regexp {explain:} $res] \
|
||||
[regexp {headers:} $res] \
|
||||
@@ -679,7 +669,7 @@ do_test shell1-3.23b.4 {
|
||||
# Adverse interaction between .stats and .eqp
|
||||
#
|
||||
do_test shell1-3.23b.5 {
|
||||
catchcmd "test.db" [string map {"\n " "\n"} {
|
||||
catchcmd "test.db" [string map {"\n " "\n"} {.mode batch
|
||||
CREATE TEMP TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1),(2);
|
||||
.stats on
|
||||
@@ -741,7 +731,7 @@ do_test shell1-3.26.5 {
|
||||
do_test shell1-3.26.6 {
|
||||
catchcmd "test.db" ".mode column\n.header off\n.width -10 10\nSELECT 'abcdefg', 123456;"
|
||||
# this should be treated the same as a '1' width for col 1 and 2
|
||||
} {0 { abcdefg 123456 }}
|
||||
} {0 { abcdefg 123456}}
|
||||
|
||||
|
||||
# .timer ON|OFF Turn the CPU timer measurement on or off
|
||||
@@ -762,9 +752,9 @@ do_test shell1-3.27.4 {
|
||||
catchcmd "test.db" ".timer OFF BAD"
|
||||
} {1 {Usage: .timer on|off}}
|
||||
|
||||
do_test shell1-3-28.1 {
|
||||
do_test shell1-3.28.1 {
|
||||
catchcmd test.db \
|
||||
".log stdout\nSELECT coalesce(sqlite_log(123,'hello'),'456');"
|
||||
".mode batch\n.log stdout\nSELECT coalesce(sqlite_log(123,'hello'),'456');"
|
||||
} "0 {(123) hello\n456}"
|
||||
|
||||
do_test shell1-3-29.1 {
|
||||
@@ -803,14 +793,14 @@ INSERT INTO t1 VALUES('');
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2.25);
|
||||
INSERT INTO t1 VALUES('hello');
|
||||
INSERT INTO t1 VALUES(X'807f');
|
||||
INSERT INTO t1 VALUES(x'807f');
|
||||
CREATE TABLE t3(x,y);
|
||||
INSERT INTO t3 VALUES(1,NULL);
|
||||
INSERT INTO t3 VALUES(2,'');
|
||||
INSERT INTO t3 VALUES(3,1);
|
||||
INSERT INTO t3 VALUES(4,2.25);
|
||||
INSERT INTO t3 VALUES(5,'hello');
|
||||
INSERT INTO t3 VALUES(6,X'807f');
|
||||
INSERT INTO t3 VALUES(6,x'807f');
|
||||
COMMIT;}}
|
||||
|
||||
|
||||
@@ -828,14 +818,14 @@ INSERT INTO t1(rowid,x) VALUES(2,'');
|
||||
INSERT INTO t1(rowid,x) VALUES(3,1);
|
||||
INSERT INTO t1(rowid,x) VALUES(4,2.25);
|
||||
INSERT INTO t1(rowid,x) VALUES(5,'hello');
|
||||
INSERT INTO t1(rowid,x) VALUES(6,X'807f');
|
||||
INSERT INTO t1(rowid,x) VALUES(6,x'807f');
|
||||
CREATE TABLE t3(x,y);
|
||||
INSERT INTO t3(rowid,x,y) VALUES(1,1,NULL);
|
||||
INSERT INTO t3(rowid,x,y) VALUES(2,2,'');
|
||||
INSERT INTO t3(rowid,x,y) VALUES(3,3,1);
|
||||
INSERT INTO t3(rowid,x,y) VALUES(4,4,2.25);
|
||||
INSERT INTO t3(rowid,x,y) VALUES(5,5,'hello');
|
||||
INSERT INTO t3(rowid,x,y) VALUES(6,6,X'807f');
|
||||
INSERT INTO t3(rowid,x,y) VALUES(6,6,x'807f');
|
||||
COMMIT;}}
|
||||
|
||||
# If the table contains an INTEGER PRIMARY KEY, do not record a separate
|
||||
@@ -854,12 +844,12 @@ do_test shell1-4.1.2 {
|
||||
} {0 {PRAGMA foreign_keys=OFF;
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
|
||||
INSERT INTO t1 VALUES(1,NULL);
|
||||
INSERT INTO t1 VALUES(2,'');
|
||||
INSERT INTO t1 VALUES(3,1);
|
||||
INSERT INTO t1 VALUES(4,2.25);
|
||||
INSERT INTO t1 VALUES(5,'hello');
|
||||
INSERT INTO t1 VALUES(6,X'807f');
|
||||
INSERT INTO t1(x,y) VALUES(1,NULL);
|
||||
INSERT INTO t1(x,y) VALUES(2,'');
|
||||
INSERT INTO t1(x,y) VALUES(3,1);
|
||||
INSERT INTO t1(x,y) VALUES(4,2.25);
|
||||
INSERT INTO t1(x,y) VALUES(5,'hello');
|
||||
INSERT INTO t1(x,y) VALUES(6,x'807f');
|
||||
COMMIT;}}
|
||||
|
||||
# Verify that the table named [table] is correctly quoted and that
|
||||
@@ -883,7 +873,7 @@ INSERT INTO "table"(rowid,x,y) VALUES(2,12,'');
|
||||
INSERT INTO "table"(rowid,x,y) VALUES(3,23,1);
|
||||
INSERT INTO "table"(rowid,x,y) VALUES(4,34,2.25);
|
||||
INSERT INTO "table"(rowid,x,y) VALUES(5,45,'hello');
|
||||
INSERT INTO "table"(rowid,x,y) VALUES(6,56,X'807f');
|
||||
INSERT INTO "table"(rowid,x,y) VALUES(6,56,x'807f');
|
||||
COMMIT;}}
|
||||
|
||||
# Do not record rowids for a WITHOUT ROWID table. Also check correct quoting
|
||||
@@ -902,12 +892,12 @@ do_test shell1-4.1.4 {
|
||||
} {0 {PRAGMA foreign_keys=OFF;
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE [ta<>ble](x INTEGER PRIMARY KEY, y) WITHOUT ROWID;
|
||||
INSERT INTO "ta<>ble" VALUES(1,NULL);
|
||||
INSERT INTO "ta<>ble" VALUES(12,'');
|
||||
INSERT INTO "ta<>ble" VALUES(23,1);
|
||||
INSERT INTO "ta<>ble" VALUES(34,2.25);
|
||||
INSERT INTO "ta<>ble" VALUES(45,'hello');
|
||||
INSERT INTO "ta<>ble" VALUES(56,X'807f');
|
||||
INSERT INTO "ta<>ble"(x,y) VALUES(1,NULL);
|
||||
INSERT INTO "ta<>ble"(x,y) VALUES(12,'');
|
||||
INSERT INTO "ta<>ble"(x,y) VALUES(23,1);
|
||||
INSERT INTO "ta<>ble"(x,y) VALUES(34,2.25);
|
||||
INSERT INTO "ta<>ble"(x,y) VALUES(45,'hello');
|
||||
INSERT INTO "ta<>ble"(x,y) VALUES(56,x'807f');
|
||||
COMMIT;}}
|
||||
|
||||
# Do not record rowids if the rowid is inaccessible
|
||||
@@ -924,9 +914,9 @@ do_test shell1-4.1.5 {
|
||||
} {0 {PRAGMA foreign_keys=OFF;
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE t1(_ROWID_,rowid,oid);
|
||||
INSERT INTO t1 VALUES(1,NULL,'alpha');
|
||||
INSERT INTO t1 VALUES(12,'',99);
|
||||
INSERT INTO t1 VALUES(23,1,X'b0b1b2');
|
||||
INSERT INTO t1(_ROWID_,rowid,oid) VALUES(1,NULL,'alpha');
|
||||
INSERT INTO t1(_ROWID_,rowid,oid) VALUES(12,'',99);
|
||||
INSERT INTO t1(_ROWID_,rowid,oid) VALUES(23,1,x'b0b1b2');
|
||||
COMMIT;}}
|
||||
|
||||
} else {
|
||||
@@ -1020,7 +1010,7 @@ INSERT INTO t1 VALUES('');
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2.25);
|
||||
INSERT INTO t1 VALUES('hello');
|
||||
INSERT INTO t1 VALUES(X'807f');}}
|
||||
INSERT INTO t1 VALUES(x'807f');}}
|
||||
|
||||
# Test the output of ".mode insert" with headers
|
||||
#
|
||||
@@ -1031,7 +1021,7 @@ INSERT INTO t1(x) VALUES('');
|
||||
INSERT INTO t1(x) VALUES(1);
|
||||
INSERT INTO t1(x) VALUES(2.25);
|
||||
INSERT INTO t1(x) VALUES('hello');
|
||||
INSERT INTO t1(x) VALUES(X'807f');}}
|
||||
INSERT INTO t1(x) VALUES(x'807f');}}
|
||||
|
||||
# Test the output of ".mode insert"
|
||||
#
|
||||
@@ -1042,7 +1032,7 @@ INSERT INTO t3 VALUES(2,'');
|
||||
INSERT INTO t3 VALUES(3,1);
|
||||
INSERT INTO t3 VALUES(4,2.25);
|
||||
INSERT INTO t3 VALUES(5,'hello');
|
||||
INSERT INTO t3 VALUES(6,X'807f');}}
|
||||
INSERT INTO t3 VALUES(6,x'807f');}}
|
||||
|
||||
# Test the output of ".mode insert" with headers
|
||||
#
|
||||
@@ -1053,7 +1043,7 @@ INSERT INTO t3(x,y) VALUES(2,'');
|
||||
INSERT INTO t3(x,y) VALUES(3,1);
|
||||
INSERT INTO t3(x,y) VALUES(4,2.25);
|
||||
INSERT INTO t3(x,y) VALUES(5,'hello');
|
||||
INSERT INTO t3(x,y) VALUES(6,X'807f');}}
|
||||
INSERT INTO t3(x,y) VALUES(6,x'807f');}}
|
||||
|
||||
# Test the output of ".mode tcl"
|
||||
#
|
||||
@@ -1069,8 +1059,8 @@ do_test shell1-4.3 {
|
||||
catchcmd test.db ".mode tcl\nselect * from t1;"
|
||||
} {0 {""
|
||||
""
|
||||
"1"
|
||||
"2.25"
|
||||
1
|
||||
2.25
|
||||
"hello"
|
||||
"\200\177"}}
|
||||
|
||||
@@ -1083,15 +1073,15 @@ do_test shell1-4.4 {
|
||||
}
|
||||
catchcmd test.db ".mode tcl\nselect * from t2;"
|
||||
} {0 {"" ""
|
||||
"1" "2.25"
|
||||
1 2.25
|
||||
"hello" "\200\177"}}
|
||||
|
||||
# Test the output of ".mode tcl" with ".nullvalue"
|
||||
#
|
||||
do_test shell1-4.5 {
|
||||
catchcmd test.db ".mode tcl\n.nullvalue NULL\nselect * from t2;"
|
||||
} {0 {"NULL" ""
|
||||
"1" "2.25"
|
||||
} {0 {NULL ""
|
||||
1 2.25
|
||||
"hello" "\200\177"}}
|
||||
|
||||
# Test the output of ".mode tcl" with Tcl reserved characters
|
||||
@@ -1115,7 +1105,7 @@ do_test shell1-4.6 {
|
||||
#
|
||||
do_test shell1-4.7 {
|
||||
catchcmd test.db ".mode quote\nselect x'0123456789ABCDEF';"
|
||||
} {0 X'0123456789abcdef'}
|
||||
} {0 x'0123456789abcdef'}
|
||||
|
||||
# Test using arbitrary byte data with the shell via standard input/output.
|
||||
#
|
||||
@@ -1274,7 +1264,7 @@ do_test shell1-7.1.7 {
|
||||
# information.
|
||||
#
|
||||
do_test shell1-8.1 {
|
||||
catchcmd ":memory:" {
|
||||
catchcmd ":memory:" {.mode batch
|
||||
-- The pow2 table will hold all the necessary powers of two.
|
||||
CREATE TABLE pow2(x INTEGER PRIMARY KEY, v TEXT);
|
||||
WITH RECURSIVE c(x,v) AS (
|
||||
@@ -1326,6 +1316,7 @@ do_test_with_ansi_output shell1-8.5 {
|
||||
create table t(a text, b int);
|
||||
insert into t values ('too long for one line', 1), ('shorter', NULL);
|
||||
.header on
|
||||
.mode box --wordwrap off
|
||||
.width 10 10
|
||||
.nullvalue NADA
|
||||
select * from t;}
|
||||
@@ -1344,12 +1335,12 @@ select * from t;}
|
||||
#
|
||||
do_test shell1-9.1 {
|
||||
catchcmd :memory: {
|
||||
.mode csv
|
||||
.mode csv --rowsep "\n"
|
||||
/*
|
||||
x */ select 1,2; --x
|
||||
-- .nada
|
||||
;
|
||||
.mode csv
|
||||
.mode csv --rowsep "\n"
|
||||
--x
|
||||
select 2,1; select 3,4;
|
||||
}
|
||||
@@ -1387,4 +1378,30 @@ select base85(zeroblob(2000000000));
|
||||
}
|
||||
} {/1.*too big.*/}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# As of 2025-11-17, the default mode is:
|
||||
#
|
||||
# qbox --screenwidth auto --linelimit 5 --charlimit 300 --textjsonb on
|
||||
#
|
||||
do_test shell1-12.1 {
|
||||
catchcmd :memory: {.mode tty
|
||||
.print
|
||||
SELECT jsonb(1234) AS x;}
|
||||
} {0 {
|
||||
┌───────────────┐
|
||||
│ x │
|
||||
├───────────────┤
|
||||
│ jsonb('1234') │
|
||||
└───────────────┘}}
|
||||
do_test shell1-12.2 {
|
||||
catchcmd :memory: {.mode box --textjsonb on
|
||||
.print
|
||||
SELECT jsonb(1234) AS x;}
|
||||
} {0 {
|
||||
┌──────┐
|
||||
│ x │
|
||||
├──────┤
|
||||
│ 1234 │
|
||||
└──────┘}}
|
||||
|
||||
finish_test
|
||||
|
||||
+79
-9
@@ -44,7 +44,7 @@ do_test shell2-1.1.1 {
|
||||
# Shell silently ignores extra parameters.
|
||||
# Ticket [f5cb008a65].
|
||||
do_test shell2-1.2.1 {
|
||||
catchcmdex {:memory: "select+3" "select+4"}
|
||||
catchcmdex {:memory: -list "select+3" "select+4"}
|
||||
} {0 {3
|
||||
4
|
||||
}}
|
||||
@@ -64,7 +64,7 @@ do_test shell2-1.3 {
|
||||
|
||||
UPDATE OR REPLACE t5 SET a = 4 WHERE a = 1;
|
||||
}
|
||||
} {1 {Runtime error near line 9: too many levels of trigger recursion}}
|
||||
} {1 {Error near line 9: too many levels of trigger recursion}}
|
||||
|
||||
|
||||
|
||||
@@ -75,7 +75,8 @@ do_test shell2-1.3 {
|
||||
# NB. whitespace is important
|
||||
do_test shell2-1.4.1 {
|
||||
forcedelete foo.db
|
||||
catchcmd "foo.db" {CREATE TABLE foo(a);
|
||||
catchcmd "foo.db" {.mode batch
|
||||
CREATE TABLE foo(a);
|
||||
INSERT INTO foo(a) VALUES(1);
|
||||
SELECT * FROM foo;}
|
||||
} {0 1}
|
||||
@@ -84,7 +85,7 @@ SELECT * FROM foo;}
|
||||
# NB. whitespace is important
|
||||
do_test shell2-1.4.2 {
|
||||
forcedelete foo.db
|
||||
catchcmd "-echo foo.db" {CREATE TABLE foo(a);
|
||||
catchcmd "-compat 20250101 -echo foo.db" {CREATE TABLE foo(a);
|
||||
INSERT INTO foo(a) VALUES(1);
|
||||
SELECT * FROM foo;}
|
||||
} {0 {CREATE TABLE foo(a);
|
||||
@@ -96,7 +97,9 @@ SELECT * FROM foo;
|
||||
# NB. whitespace is important
|
||||
do_test shell2-1.4.3 {
|
||||
forcedelete foo.db
|
||||
catchcmd "foo.db" {.echo ON
|
||||
catchcmd "foo.db" {
|
||||
.mode batch
|
||||
.echo ON
|
||||
CREATE TABLE foo(a);
|
||||
INSERT INTO foo(a) VALUES(1);
|
||||
SELECT * FROM foo;}
|
||||
@@ -110,7 +113,9 @@ SELECT * FROM foo;
|
||||
# NB. whitespace is important
|
||||
do_test shell2-1.4.4 {
|
||||
forcedelete foo.db
|
||||
catchcmd "foo.db" {.echo ON
|
||||
catchcmd "foo.db" {
|
||||
.mode batch
|
||||
.echo ON
|
||||
CREATE TABLE foo(a);
|
||||
.echo OFF
|
||||
INSERT INTO foo(a) VALUES(1);
|
||||
@@ -124,7 +129,9 @@ SELECT * FROM foo;}
|
||||
# NB. whitespace is important
|
||||
do_test shell2-1.4.5 {
|
||||
forcedelete foo.db
|
||||
catchcmdex "foo.db" {.echo ON
|
||||
catchcmdex "foo.db" {
|
||||
.mode batch
|
||||
.echo ON
|
||||
CREATE TABLE foo1(a);
|
||||
INSERT INTO foo1(a) VALUES(1);
|
||||
CREATE TABLE foo2(b);
|
||||
@@ -153,7 +160,9 @@ SELECT * FROM foo1; SELECT * FROM foo2;
|
||||
# NB. whitespace is important
|
||||
do_test shell2-1.4.6 {
|
||||
forcedelete foo.db
|
||||
catchcmdex "foo.db" {.echo ON
|
||||
catchcmdex "foo.db" {
|
||||
.mode batch
|
||||
.echo ON
|
||||
.headers ON
|
||||
CREATE TABLE foo1(a);
|
||||
INSERT INTO foo1(a) VALUES(1);
|
||||
@@ -208,6 +217,7 @@ do_test shell2-1.4.9 {
|
||||
do_test shell2-1.4.9 {
|
||||
forcedelete clone.db
|
||||
set res [catchcmd :memory: [string trim {
|
||||
.mode batch
|
||||
CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT);
|
||||
INSERT INTO t VALUES (1),(2);
|
||||
.clone clone.db
|
||||
@@ -222,6 +232,7 @@ ifcapable vtab {
|
||||
# See overflow report at https://sqlite.org/forum/forumpost/5d34ce5280
|
||||
do_test shell2-1.4.10 {
|
||||
set res [catchcmd :memory: [string trim {
|
||||
.mode batch
|
||||
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,1);
|
||||
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,-1);
|
||||
SELECT avg(value),min(value),max(value) FROM generate_series(
|
||||
@@ -248,6 +259,65 @@ do_test shell2-1.4.10 {
|
||||
0
|
||||
1
|
||||
2}}
|
||||
do_test shell2-1.4.10b {
|
||||
set res [catchcmd :memory: [string trim {
|
||||
.mode tty
|
||||
.print
|
||||
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,1);
|
||||
SELECT * FROM generate_series(9223372036854775807,9223372036854775807,-1);
|
||||
SELECT avg(value),min(value),max(value) FROM generate_series(
|
||||
-9223372036854775808,9223372036854775807,1085102592571150095);
|
||||
SELECT * FROM generate_series(-9223372036854775808,9223372036854775807,
|
||||
9223372036854775807);
|
||||
SELECT value FROM generate_series(-4611686018427387904,
|
||||
4611686018427387904, 4611686018427387904) ORDER BY value DESC;
|
||||
SELECT * FROM generate_series(0,-2,-1);
|
||||
SELECT * FROM generate_series(0,-2);
|
||||
SELECT * FROM generate_series(0,2) LIMIT 3;}]]
|
||||
} {0 {
|
||||
┌─────────────────────┐
|
||||
│ value │
|
||||
├─────────────────────┤
|
||||
│ 9223372036854775807 │
|
||||
└─────────────────────┘
|
||||
┌─────────────────────┐
|
||||
│ value │
|
||||
├─────────────────────┤
|
||||
│ 9223372036854775807 │
|
||||
└─────────────────────┘
|
||||
┌────────────┬──────────────────────┬─────────────────────┐
|
||||
│ avg(value) │ min(value) │ max(value) │
|
||||
├────────────┼──────────────────────┼─────────────────────┤
|
||||
│ -0.5 │ -9223372036854775808 │ 9223372036854775807 │
|
||||
└────────────┴──────────────────────┴─────────────────────┘
|
||||
┌──────────────────────┐
|
||||
│ value │
|
||||
├──────────────────────┤
|
||||
│ -9223372036854775808 │
|
||||
│ -1 │
|
||||
│ 9223372036854775806 │
|
||||
└──────────────────────┘
|
||||
┌──────────────────────┐
|
||||
│ value │
|
||||
├──────────────────────┤
|
||||
│ 4611686018427387904 │
|
||||
│ 0 │
|
||||
│ -4611686018427387904 │
|
||||
└──────────────────────┘
|
||||
┌───────┐
|
||||
│ value │
|
||||
├───────┤
|
||||
│ 0 │
|
||||
│ -1 │
|
||||
│ -2 │
|
||||
└───────┘
|
||||
┌───────┐
|
||||
│ value │
|
||||
├───────┤
|
||||
│ 0 │
|
||||
│ 1 │
|
||||
│ 2 │
|
||||
└───────┘}}
|
||||
} ;# ifcapable vtab
|
||||
|
||||
ifcapable vtab {
|
||||
@@ -270,7 +340,7 @@ do_test shell2-1.4.11 {
|
||||
# Bug from forum post 7cbe081746dd3803
|
||||
# Keywords as column names were producing an error message.
|
||||
do_test shell2-1.4.12 {
|
||||
set res [catchcmd :memory: [string trim {
|
||||
set res [catchcmd :memory: [string trim {.mode batch
|
||||
CREATE TABLE "group"("order" text);
|
||||
INSERT INTO "group" VALUES ('ABC');
|
||||
.sha3sum}]]
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ do_test shell3-2.7 {
|
||||
# Run combinations of odd identifiers, comments, semicolon placement
|
||||
do_test shell3-3.1 {
|
||||
forcedelete foo.db
|
||||
set rc [ catchcmd "foo.db" {CREATE TABLE t1("
|
||||
set rc [ catchcmd "foo.db -compat 20250101" {CREATE TABLE t1("
|
||||
a--.
|
||||
" --x
|
||||
); CREATE TABLE t2("a[""b""]");
|
||||
|
||||
+4
-4
@@ -136,15 +136,15 @@ SELECT * FROM t1;}
|
||||
|
||||
do_test shell4-3.1 {
|
||||
set fd [open t1.txt wb]
|
||||
puts $fd "SELECT 'squirrel';"
|
||||
puts $fd ".mode list\nSELECT 'squirrel';"
|
||||
close $fd
|
||||
exec $::CLI_ONLY :memory: --interactive ".read t1.txt"
|
||||
exec $::CLI_ONLY --noinit :memory: --interactive ".read t1.txt"
|
||||
} {squirrel}
|
||||
do_test_with_ansi_output shell4-3.2 {
|
||||
set fd [open t1.txt wb]
|
||||
puts $fd "SELECT 'pound: \302\243';"
|
||||
puts $fd ".mode list\nSELECT 'pound: \302\243';"
|
||||
close $fd
|
||||
exec $::CLI_ONLY :memory: --interactive ".read t1.txt"
|
||||
exec $::CLI_ONLY --noinit :memory: --interactive ".read t1.txt"
|
||||
} {pound: £}
|
||||
|
||||
do_test shell4-4.1 {
|
||||
|
||||
+29
-21
@@ -40,7 +40,8 @@ do_test shell5-1.1.2 {
|
||||
do_test shell5-1.1.3 {
|
||||
# too many arguments
|
||||
catchcmd "test.db" ".import FOO BAR BAD"
|
||||
} {/1 .ERROR: extra argument.*/}
|
||||
} {1 {line 1: .import FOO BAR BAD
|
||||
line 1: ^--- unknown argument}}
|
||||
|
||||
# .separator STRING Change separator used by output mode and .import
|
||||
do_test shell5-1.2.1 {
|
||||
@@ -59,13 +60,13 @@ do_test shell5-1.2.4 {
|
||||
|
||||
# column separator should default to "|"
|
||||
do_test shell5-1.3.1.1 {
|
||||
set res [catchcmd "test.db" ".show"]
|
||||
set res [catchcmd "test.db" ".mode list\n.show"]
|
||||
list [regexp {colseparator: \"\|\"} $res]
|
||||
} {1}
|
||||
|
||||
# row separator should default to "\n"
|
||||
do_test shell5-1.3.1.2 {
|
||||
set res [catchcmd "test.db" ".show"]
|
||||
set res [catchcmd "test.db" ".mode list\n.show"]
|
||||
list [regexp {rowseparator: \"\\n\"} $res]
|
||||
} {1}
|
||||
|
||||
@@ -97,7 +98,9 @@ do_test shell5-1.4.2 {
|
||||
forcedelete shell5.csv
|
||||
set in [open shell5.csv w]
|
||||
close $in
|
||||
set res [catchcmd ":memory:" {ATTACH 'test.db' AS test;
|
||||
set res [catchcmd ":memory:" {
|
||||
.mode list
|
||||
ATTACH 'test.db' AS test;
|
||||
.import -schema test shell5.csv t1
|
||||
SELECT COUNT(*) FROM test.t1;}]
|
||||
} {0 0}
|
||||
@@ -116,7 +119,7 @@ do_test shell5-1.4.4 {
|
||||
set in [open shell5.csv w]
|
||||
puts $in "1|2|3"
|
||||
close $in
|
||||
set res [catchcmd ":memory:" {ATTACH 'test.db' AS test;
|
||||
set res [catchcmd ":memory: --list" {ATTACH 'test.db' AS test;
|
||||
.import --schema test shell5.csv t1}]
|
||||
} {1 {shell5.csv:1: expected 2 columns but found 3 - extras ignored}}
|
||||
|
||||
@@ -125,7 +128,7 @@ do_test shell5-1.4.5 {
|
||||
set in [open shell5.csv w]
|
||||
puts $in "1|2"
|
||||
close $in
|
||||
set res [catchcmd "test.db" {DELETE FROM t1;
|
||||
set res [catchcmd "test.db -list" {DELETE FROM t1;
|
||||
.import shell5.csv t1
|
||||
SELECT COUNT(*) FROM t1;}]
|
||||
} {0 1}
|
||||
@@ -138,7 +141,9 @@ do_test shell5-1.4.6 {
|
||||
puts $in "2|3"
|
||||
puts $in "3|4"
|
||||
close $in
|
||||
set res [catchcmd ":memory:" {ATTACH 'test.db' AS test;
|
||||
set res [catchcmd ":memory:" {
|
||||
.mode list
|
||||
ATTACH 'test.db' AS test;
|
||||
.import -schema test shell5.csv t1
|
||||
SELECT COUNT(*) FROM test.t1;}]
|
||||
} {0 3}
|
||||
@@ -148,7 +153,9 @@ do_test shell5-1.4.7 {
|
||||
set in [open shell5.csv w]
|
||||
puts $in "4,5"
|
||||
close $in
|
||||
set res [catchcmd ":memory:" {ATTACH 'test.db' AS test;
|
||||
set res [catchcmd ":memory:" {
|
||||
.mode list
|
||||
ATTACH 'test.db' AS test;
|
||||
.separator ,
|
||||
.import --schema test shell5.csv t1
|
||||
SELECT COUNT(*) FROM test.t1;}]
|
||||
@@ -159,12 +166,13 @@ do_test shell5-1.4.8.1 {
|
||||
set in [open shell5.csv w]
|
||||
puts $in "5|Now is the time for all good men to come to the aid of their country."
|
||||
close $in
|
||||
set res [catchcmd "test.db" {.import shell5.csv t1
|
||||
set res [catchcmd "test.db" {.mode list
|
||||
.import shell5.csv t1
|
||||
SELECT COUNT(*) FROM t1;}]
|
||||
} {0 5}
|
||||
|
||||
do_test shell5-1.4.8.2 {
|
||||
catchcmd "test.db" {SELECT b FROM t1 WHERE a='5';}
|
||||
catchcmd "test.db -list" {SELECT b FROM t1 WHERE a='5';}
|
||||
} {0 {Now is the time for all good men to come to the aid of their country.}}
|
||||
|
||||
# import file with 1 row, 2 columns, quoted text data
|
||||
@@ -174,12 +182,12 @@ do_test shell5-1.4.9.1 {
|
||||
set in [open shell5.csv w]
|
||||
puts $in "6|'Now is the time for all good men to come to the aid of their country.'"
|
||||
close $in
|
||||
set res [catchcmd "test.db" {.import shell5.csv t1
|
||||
set res [catchcmd "test.db -list" {.import shell5.csv t1
|
||||
SELECT COUNT(*) FROM t1;}]
|
||||
} {0 6}
|
||||
|
||||
do_test shell5-1.4.9.2 {
|
||||
catchcmd "test.db" {SELECT b FROM t1 WHERE a='6';}
|
||||
catchcmd "test.db -list" {SELECT b FROM t1 WHERE a='6';}
|
||||
} {0 {'Now is the time for all good men to come to the aid of their country.'}}
|
||||
|
||||
# import file with 1 row, 2 columns, quoted text data
|
||||
@@ -187,12 +195,12 @@ do_test shell5-1.4.10.1 {
|
||||
set in [open shell5.csv w]
|
||||
puts $in "7|\"Now is the time for all good men to come to the aid of their country.\""
|
||||
close $in
|
||||
set res [catchcmd "test.db" {.import shell5.csv t1
|
||||
set res [catchcmd "test.db -list" {.import shell5.csv t1
|
||||
SELECT COUNT(*) FROM t1;}]
|
||||
} {0 7}
|
||||
|
||||
do_test shell5-1.4.10.2 {
|
||||
catchcmd "test.db" {SELECT b FROM t1 WHERE a='7';}
|
||||
catchcmd "test.db -list" {SELECT b FROM t1 WHERE a='7';}
|
||||
} {0 {Now is the time for all good men to come to the aid of their country.}}
|
||||
|
||||
# import file with 2 rows, 2 columns and an initial BOM
|
||||
@@ -203,7 +211,7 @@ do_test shell5-1.4.11 {
|
||||
puts $in "2|3"
|
||||
puts $in "4|5"
|
||||
close $in
|
||||
set res [catchcmd "test.db" {CREATE TABLE t2(x INT, y INT);
|
||||
set res [catchcmd "test.db -list" {CREATE TABLE t2(x INT, y INT);
|
||||
.import shell5.csv t2
|
||||
.mode quote
|
||||
.header on
|
||||
@@ -218,7 +226,7 @@ do_test shell5-1.4.12 {
|
||||
puts $in "\xef\xbb\xbf\"two\"|3"
|
||||
puts $in "4|5"
|
||||
close $in
|
||||
set res [catchcmd "test.db" {DELETE FROM t2;
|
||||
set res [catchcmd "test.db -list" {DELETE FROM t2;
|
||||
.import shell5.csv t2
|
||||
.mode quote
|
||||
.header on
|
||||
@@ -232,7 +240,7 @@ do_test shell5-1.5.1 {
|
||||
set in [open shell5.csv w]
|
||||
puts $in "8|$str"
|
||||
close $in
|
||||
set res [catchcmd "test.db" {.import shell5.csv t1
|
||||
set res [catchcmd "test.db -list" {.import shell5.csv t1
|
||||
SELECT length(b) FROM t1 WHERE a='8';}]
|
||||
} {0 999}
|
||||
|
||||
@@ -252,7 +260,7 @@ do_test shell5-1.6.1 {
|
||||
set in [open shell5.csv w]
|
||||
puts $in $data
|
||||
close $in
|
||||
set res [catchcmd "test.db" {DROP TABLE IF EXISTS t2;
|
||||
set res [catchcmd "test.db -list" {DROP TABLE IF EXISTS t2;
|
||||
.import shell5.csv t2
|
||||
SELECT COUNT(*) FROM t2;}]
|
||||
} {0 1}
|
||||
@@ -576,8 +584,8 @@ do_test_with_ansi_output shell5-6.2 {
|
||||
catchcmd test.db {.import -csv shell5.csv t1
|
||||
.mode line
|
||||
SELECT * FROM t1;}
|
||||
} {0 { 1 = あい
|
||||
2 = うえお}}
|
||||
} {0 {1 = あい
|
||||
2 = うえお}}
|
||||
|
||||
# 2024-03-11 https://sqlite.org/forum/forumpost/ca014d7358
|
||||
# Import into a table that contains computed columns.
|
||||
@@ -588,7 +596,7 @@ do_test shell5-7.1 {
|
||||
puts $out {aaa|bbb}
|
||||
close $out
|
||||
forcedelete test.db
|
||||
catchcmd :memory: {CREATE TABLE t1(a TEXT, b TEXT, c AS (a||b));
|
||||
catchcmd ":memory: -list" {CREATE TABLE t1(a TEXT, b TEXT, c AS (a||b));
|
||||
.import shell5.csv t1
|
||||
SELECT * FROM t1;}
|
||||
} {0 aaa|bbb|aaabbb}
|
||||
|
||||
+106
-31
@@ -36,7 +36,7 @@ do_execsql_test shellA-1.0 {
|
||||
# and that our calls to the CLI are working.
|
||||
#
|
||||
do_test_with_ansi_output shellA-1.2 {
|
||||
exec {*}$CLI test.db {.mode box --escape symbol} {SELECT * FROM t1;}
|
||||
exec {*}$CLI -noinit test.db {.mode box -quote off --escape symbol} {SELECT * FROM t1;}
|
||||
} {
|
||||
┌───┬──────────────────────────┐
|
||||
│ a │ x │
|
||||
@@ -63,33 +63,33 @@ do_test_with_ansi_output shellA-1.2 {
|
||||
# ".mode list"
|
||||
#
|
||||
do_test shellA-1.3 {
|
||||
exec {*}$CLI test.db {SELECT x FROM t1 WHERE a=2;}
|
||||
exec {*}$CLI -noinit -list test.db {SELECT x FROM t1 WHERE a=2;}
|
||||
} {
|
||||
^[[31mVT-100 codes^[[0m
|
||||
}
|
||||
do_test_with_ansi_output shellA-1.4 {
|
||||
exec {*}$CLI test.db --escape symbol {SELECT x FROM t1 WHERE a=2;}
|
||||
exec {*}$CLI -noinit -list test.db --escape symbol {SELECT x FROM t1 WHERE a=2;}
|
||||
} {
|
||||
␛[31mVT-100 codes␛[0m
|
||||
}
|
||||
do_test shellA-1.5 {
|
||||
exec {*}$CLI test.db --escape ascii {SELECT x FROM t1 WHERE a=2;}
|
||||
exec {*}$CLI -noinit -list test.db --escape ascii {SELECT x FROM t1 WHERE a=2;}
|
||||
} {
|
||||
^[[31mVT-100 codes^[[0m
|
||||
}
|
||||
do_test_with_ansi_output shellA-1.6 {
|
||||
exec {*}$CLI test.db {.mode list --escape symbol} {SELECT x FROM t1 WHERE a=2;}
|
||||
exec {*}$CLI -noinit test.db {.mode list --escape symbol} {SELECT x FROM t1 WHERE a=2;}
|
||||
} {
|
||||
␛[31mVT-100 codes␛[0m
|
||||
}
|
||||
do_test shellA-1.7 {
|
||||
exec {*}$CLI test.db {.mode list --escape ascii} {SELECT x FROM t1 WHERE a=2;}
|
||||
exec {*}$CLI -noinit test.db {.mode list --escape ascii} {SELECT x FROM t1 WHERE a=2;}
|
||||
} {
|
||||
^[[31mVT-100 codes^[[0m
|
||||
}
|
||||
do_test shellA-1.8 {
|
||||
file delete -force out.txt
|
||||
exec {*}$CLI test.db {.mode list --escape off} {SELECT x FROM t1 WHERE a=7;} \
|
||||
exec {*}$CLI -noinit test.db {.mode list --escape off} {SELECT x FROM t1 WHERE a=7;} \
|
||||
>out.txt
|
||||
set fd [open out.txt rb]
|
||||
set res [read $fd]
|
||||
@@ -98,44 +98,45 @@ do_test shellA-1.8 {
|
||||
} "carriage\rreturn"
|
||||
do_test shellA-1.9 {
|
||||
set rc [catch {
|
||||
exec {*}$CLI test.db {.mode test --escape xyz}
|
||||
exec {*}$CLI -noinit test.db {.mode test --escape xyz}
|
||||
} msg]
|
||||
lappend rc $msg
|
||||
} {1 {unknown control character escape mode "xyz" - choices: ascii symbol off}}
|
||||
} {1 {argv[3]: .mode test --escape xyz
|
||||
argv[3]: ^--- unknown mode
|
||||
argv[3]: Use ".help .mode" for more info}}
|
||||
do_test shellA-1.10 {
|
||||
set rc [catch {
|
||||
exec {*}$CLI --escape abc test.db .q
|
||||
exec {*}$CLI --noinit --escape abc test.db .q
|
||||
} msg]
|
||||
lappend rc $msg
|
||||
} {1 {unknown control character escape mode "abc" - choices: ascii symbol off}}
|
||||
} {1 {unknown control character escape mode "abc" - choices: auto off ascii symbol}}
|
||||
|
||||
# ".mode quote"
|
||||
#
|
||||
do_test shellA-2.1 {
|
||||
exec {*}$CLI test.db --quote {SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
|
||||
exec {*}$CLI -noinit test.db --quote {SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
|
||||
} {
|
||||
1,'line with '' single quote'
|
||||
2,unistr('\u001b[31mVT-100 codes\u001b[0m')
|
||||
6,'new
|
||||
line'
|
||||
6,unistr('new\u000aline')
|
||||
7,unistr('carriage\u000dreturn')
|
||||
8,'last line'
|
||||
}
|
||||
do_test shellA-2.2 {
|
||||
exec {*}$CLI test.db --quote {.mode}
|
||||
} {current output mode: quote --escape ascii}
|
||||
exec {*}$CLI -noinit test.db --quote {.mode -v}
|
||||
} {/*current output mode: quote* --escape auto*/}
|
||||
do_test shellA-2.3 {
|
||||
exec {*}$CLI test.db --quote --escape SYMBOL {.mode}
|
||||
exec {*}$CLI -noinit -compat 20250101 test.db --quote --escape SYMBOL {.mode}
|
||||
} {current output mode: quote --escape symbol}
|
||||
do_test shellA-2.4 {
|
||||
exec {*}$CLI test.db --quote --escape OFF {.mode}
|
||||
exec {*}$CLI -noinit -compat 20250101 test.db --quote --escape OFF {.mode}
|
||||
} {current output mode: quote --escape off}
|
||||
|
||||
|
||||
# ".mode line"
|
||||
#
|
||||
do_test_with_ansi_output shellA-3.1 {
|
||||
exec {*}$CLI test.db --line --escape symbol \
|
||||
exec {*}$CLI -noinit test.db --line --escape symbol \
|
||||
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
|
||||
} {
|
||||
a = 1
|
||||
@@ -155,7 +156,7 @@ line
|
||||
x = last line
|
||||
}
|
||||
do_test shellA-3.2 {
|
||||
exec {*}$CLI test.db --line --escape ascii \
|
||||
exec {*}$CLI -noinit test.db --line --escape ascii \
|
||||
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
|
||||
} {
|
||||
a = 1
|
||||
@@ -178,7 +179,7 @@ line
|
||||
# ".mode box"
|
||||
#
|
||||
do_test_with_ansi_output shellA-4.1 {
|
||||
exec {*}$CLI test.db --box --escape ascii \
|
||||
exec {*}$CLI -noinit test.db --box --escape ascii \
|
||||
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
|
||||
} {
|
||||
┌───┬──────────────────────────┐
|
||||
@@ -197,20 +198,15 @@ do_test_with_ansi_output shellA-4.1 {
|
||||
└───┴──────────────────────────┘
|
||||
}
|
||||
do_test_with_ansi_output shellA-4.2 {
|
||||
exec {*}$CLI test.db {.mode qbox} {SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
|
||||
exec {*}$CLI -noinit test.db {.mode qbox} {SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
|
||||
} {
|
||||
┌───┬───────────────────────────────────────────┐
|
||||
│ a │ x │
|
||||
├───┼───────────────────────────────────────────┤
|
||||
│ 1 │ 'line with '' single quote' │
|
||||
├───┼───────────────────────────────────────────┤
|
||||
│ 2 │ unistr('\u001b[31mVT-100 codes\u001b[0m') │
|
||||
├───┼───────────────────────────────────────────┤
|
||||
│ 6 │ 'new │
|
||||
│ │ line' │
|
||||
├───┼───────────────────────────────────────────┤
|
||||
│ 6 │ unistr('new\u000aline') │
|
||||
│ 7 │ unistr('carriage\u000dreturn') │
|
||||
├───┼───────────────────────────────────────────┤
|
||||
│ 8 │ 'last line' │
|
||||
└───┴───────────────────────────────────────────┘
|
||||
}
|
||||
@@ -218,7 +214,7 @@ do_test_with_ansi_output shellA-4.2 {
|
||||
# ".mode insert"
|
||||
#
|
||||
do_test shellA-5.1 {
|
||||
exec {*}$CLI test.db {.mode insert t1 --escape ascii} \
|
||||
exec {*}$CLI -noinit test.db {.mode insert t1 --escape ascii} \
|
||||
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
|
||||
} {
|
||||
INSERT INTO t1 VALUES(1,'line with '' single quote');
|
||||
@@ -228,7 +224,7 @@ INSERT INTO t1 VALUES(7,unistr('carriage\u000dreturn'));
|
||||
INSERT INTO t1 VALUES(8,'last line');
|
||||
}
|
||||
do_test shellA-5.2 {
|
||||
exec {*}$CLI test.db {.mode insert t1 --escape symbol} \
|
||||
exec {*}$CLI -noinit test.db {.mode insert t1 --escape symbol} \
|
||||
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)}
|
||||
} {
|
||||
INSERT INTO t1 VALUES(1,'line with '' single quote');
|
||||
@@ -239,7 +235,7 @@ INSERT INTO t1 VALUES(8,'last line');
|
||||
}
|
||||
do_test shellA-5.3 {
|
||||
file delete -force out.txt
|
||||
exec {*}$CLI test.db {.mode insert t1 --escape off} \
|
||||
exec {*}$CLI -noinit test.db {.mode insert t1 --escape off} \
|
||||
{SELECT a, x FROM t1 WHERE a IN (1,2,6,7,8)} >out.txt
|
||||
set fd [open out.txt rb]
|
||||
set res [read $fd]
|
||||
@@ -254,4 +250,83 @@ INSERT INTO t1 VALUES(7,'carriage\rreturn');
|
||||
INSERT INTO t1 VALUES(8,'last line');
|
||||
"
|
||||
|
||||
# ".mode split"
|
||||
#
|
||||
do_test shellA-6.1 {
|
||||
db eval {
|
||||
CREATE TABLE t2(x);
|
||||
INSERT INTO t2(x) VALUES
|
||||
('one'), ('two'), ('three'), ('four'), ('five'),
|
||||
('six'), ('seven'), ('eight'), ('nine'), ('ten'),
|
||||
('eleven'), ('twelve'), ('thirteen'), ('fourteen');
|
||||
}
|
||||
exec {*}$CLI -noinit test.db \
|
||||
{.print} \
|
||||
{.mode split -screenwidth 30} \
|
||||
{SELECT x FROM t2}
|
||||
} {
|
||||
one five nine thirteen
|
||||
two six ten fourteen
|
||||
three seven eleven
|
||||
four eight twelve}
|
||||
# 3456789 123456789 123456789
|
||||
|
||||
do_test shellA-6.2 {
|
||||
exec {*}$CLI -noinit test.db \
|
||||
{.print} \
|
||||
{.mode split -screenwidth 30} \
|
||||
{SELECT x FROM t2} \
|
||||
{.mode column -titles off} \
|
||||
{SELECT x FROM t2}
|
||||
} {
|
||||
one five nine thirteen
|
||||
two six ten fourteen
|
||||
three seven eleven
|
||||
four eight twelve
|
||||
one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
seven
|
||||
eight
|
||||
nine
|
||||
ten
|
||||
eleven
|
||||
twelve
|
||||
thirteen
|
||||
fourteen}
|
||||
|
||||
do_test shellA-6.3 {
|
||||
exec {*}$CLI -noinit test.db \
|
||||
{.print} \
|
||||
{.mode table} \
|
||||
{.mode --once split -screenwidth 30} \
|
||||
{SELECT x FROM t2} \
|
||||
{SELECT x FROM t2}
|
||||
} {
|
||||
one five nine thirteen
|
||||
two six ten fourteen
|
||||
three seven eleven
|
||||
four eight twelve
|
||||
+----------+
|
||||
| x |
|
||||
+----------+
|
||||
| one |
|
||||
| two |
|
||||
| three |
|
||||
| four |
|
||||
| five |
|
||||
| six |
|
||||
| seven |
|
||||
| eight |
|
||||
| nine |
|
||||
| ten |
|
||||
| eleven |
|
||||
| twelve |
|
||||
| thirteen |
|
||||
| fourteen |
|
||||
+----------+}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# 2025-11-12
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# TESTRUNNER: shell
|
||||
#
|
||||
# Test cases for the command-line shell using the new ".output memory"
|
||||
# feature.
|
||||
#
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set CLI [test_cli_invocation]
|
||||
|
||||
# Run an instance of the CLI on the file $name.
|
||||
# Capture the number of test cases and the number of
|
||||
# errors and increment the counts.
|
||||
#
|
||||
proc do_clitest {name} {
|
||||
set mapping [list <NAME> $::testdir/$name <CLI> $::CLI]
|
||||
set script [string map $mapping {
|
||||
catch {exec <CLI> :memory: ".read <NAME>" 2>@stdout} res
|
||||
set ntest 0
|
||||
set nerr 999
|
||||
regexp {(\d+) tests? run with (\d+) errors?} $res all ntest nerr
|
||||
set_test_counter count [expr {[set_test_counter count]+$ntest-1}]
|
||||
set_test_counter errors [expr {[set_test_counter errors]+$nerr}]
|
||||
if {$nerr==0} {set res "error count: 0"}
|
||||
set res
|
||||
}]
|
||||
# puts $script
|
||||
do_test shellB-$name $script {error count: 0}
|
||||
}
|
||||
|
||||
do_clitest modeA.clitest
|
||||
|
||||
finish_test
|
||||
+1
-1
@@ -42,7 +42,7 @@ do_test tcl-1.1.1 {
|
||||
do_test tcl-1.2 {
|
||||
set v [catch {db bogus} msg]
|
||||
lappend v $msg
|
||||
} {1 {bad option "bogus": must be authorizer, backup, bind_fallback, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, config, copy, deserialize, enable_load_extension, errorcode, erroroffset, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, serialize, status, timeout, total_changes, trace, trace_v2, transaction, unlock_notify, update_hook, version, or wal_hook}}
|
||||
} {1 {bad option "bogus": must be authorizer, backup, bind_fallback, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, config, copy, deserialize, enable_load_extension, errorcode, erroroffset, eval, exists, format, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, preupdate, profile, progress, rekey, restore, rollback_hook, serialize, status, timeout, total_changes, trace, trace_v2, transaction, unlock_notify, update_hook, version, or wal_hook}}
|
||||
do_test tcl-1.2.1 {
|
||||
set v [catch {db cache bogus} msg]
|
||||
lappend v $msg
|
||||
|
||||
@@ -364,6 +364,7 @@ set estwork(shell6.test) 3
|
||||
set estwork(shell8.test) 104
|
||||
set estwork(shell9.test) 3
|
||||
set estwork(shellA.test) 2
|
||||
set estwork(shellB.test) 2
|
||||
set estwork(shmlock.test) 27
|
||||
set estwork(sidedelete.test) 10
|
||||
set estwork(skipscan1.test) 7
|
||||
|
||||
@@ -198,7 +198,7 @@ foreach {tn pragma res} {
|
||||
750 {
|
||||
PRAGMA fullfsync = 1;
|
||||
PRAGMA synchronous = full;
|
||||
} {full|dataonly 1 full 2}
|
||||
} {full 3}
|
||||
} {
|
||||
|
||||
forcedelete test.db2
|
||||
|
||||
+41
-21
@@ -494,6 +494,7 @@ struct lemon {
|
||||
char *filename; /* Name of the input file */
|
||||
char *outname; /* Name of the current output file */
|
||||
char *tokenprefix; /* A prefix added to token names in the .h file */
|
||||
char *stackSizeLimit; /* Function to return the stack size limit */
|
||||
char *reallocFunc; /* Function to use to allocate stack space */
|
||||
char *freeFunc; /* Function to use to free stack space */
|
||||
int nconflict; /* Number of parsing conflicts */
|
||||
@@ -2638,6 +2639,9 @@ static void parseonetoken(struct pstate *psp)
|
||||
}else if( strcmp(x,"default_type")==0 ){
|
||||
psp->declargslot = &(psp->gp->vartype);
|
||||
psp->insertLineMacro = 0;
|
||||
}else if( strcmp(x,"stack_size_limit")==0 ){
|
||||
psp->declargslot = &(psp->gp->stackSizeLimit);
|
||||
psp->insertLineMacro = 0;
|
||||
}else if( strcmp(x,"realloc")==0 ){
|
||||
psp->declargslot = &(psp->gp->reallocFunc);
|
||||
psp->insertLineMacro = 0;
|
||||
@@ -3715,7 +3719,7 @@ PRIVATE int compute_action(struct lemon *lemp, struct action *ap)
|
||||
return act;
|
||||
}
|
||||
|
||||
#define LINESIZE 1000
|
||||
#define LINESIZE 10000
|
||||
/* The next cluster of routines are for reading the template file
|
||||
** and writing the results to the generated parser */
|
||||
/* The first function transfers data from "in" to "out" until
|
||||
@@ -3750,12 +3754,9 @@ PRIVATE void tplt_xfer(char *name, FILE *in, FILE *out, int *lineno)
|
||||
|
||||
/* Skip forward past the header of the template file to the first "%%"
|
||||
*/
|
||||
PRIVATE void tplt_skip_header(FILE *in, int *lineno)
|
||||
{
|
||||
PRIVATE void tplt_skip_header(FILE *in){
|
||||
char line[LINESIZE];
|
||||
while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){
|
||||
(*lineno)++;
|
||||
}
|
||||
while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){}
|
||||
}
|
||||
|
||||
/* The next function finds the template file and opens it, returning
|
||||
@@ -3825,12 +3826,14 @@ PRIVATE void tplt_linedir(FILE *out, int lineno, char *filename)
|
||||
filename++;
|
||||
}
|
||||
fprintf(out,"\"\n");
|
||||
fflush(out);
|
||||
}
|
||||
|
||||
/* Print a string to the file and keep the linenumber up to date */
|
||||
PRIVATE void tplt_print(FILE *out, struct lemon *lemp, char *str, int *lineno)
|
||||
{
|
||||
if( str==0 ) return;
|
||||
fflush(out);
|
||||
while( *str ){
|
||||
putc(*str,out);
|
||||
if( *str=='\n' ) (*lineno)++;
|
||||
@@ -3843,6 +3846,7 @@ PRIVATE void tplt_print(FILE *out, struct lemon *lemp, char *str, int *lineno)
|
||||
if (!lemp->nolinenosflag) {
|
||||
(*lineno)++; tplt_linedir(out,*lineno,lemp->outname);
|
||||
}
|
||||
fflush(out);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4407,6 +4411,13 @@ static void writeRuleText(FILE *out, struct rule *rp){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the string is not NULL and not empty.
|
||||
*/
|
||||
static int notnull(const char *z){
|
||||
return z && z[0];
|
||||
}
|
||||
|
||||
|
||||
/* Generate C source code for the parser */
|
||||
void ReportTable(
|
||||
@@ -4414,7 +4425,7 @@ void ReportTable(
|
||||
int mhflag, /* Output in makeheaders format if true */
|
||||
int sqlFlag /* Generate the *.sql file too */
|
||||
){
|
||||
FILE *out, *in, *sql;
|
||||
FILE *out, *in;
|
||||
int lineno;
|
||||
struct state *stp;
|
||||
struct action *ap;
|
||||
@@ -4439,15 +4450,8 @@ void ReportTable(
|
||||
|
||||
in = tplt_open(lemp);
|
||||
if( in==0 ) return;
|
||||
out = file_open(lemp,".c","wb");
|
||||
if( out==0 ){
|
||||
fclose(in);
|
||||
return;
|
||||
}
|
||||
if( sqlFlag==0 ){
|
||||
sql = 0;
|
||||
}else{
|
||||
sql = file_open(lemp, ".sql", "wb");
|
||||
if( sqlFlag ){
|
||||
FILE *sql = file_open(lemp, ".sql", "wb");
|
||||
if( sql==0 ){
|
||||
fclose(in);
|
||||
fclose(out);
|
||||
@@ -4515,6 +4519,12 @@ void ReportTable(
|
||||
}
|
||||
}
|
||||
fprintf(sql, "COMMIT;\n");
|
||||
fclose(sql);
|
||||
}
|
||||
out = file_open(lemp,".c","wb");
|
||||
if( out==0 ){
|
||||
fclose(in);
|
||||
return;
|
||||
}
|
||||
lineno = 1;
|
||||
|
||||
@@ -4543,7 +4553,7 @@ void ReportTable(
|
||||
}
|
||||
}
|
||||
if( lemp->include[0]=='/' ){
|
||||
tplt_skip_header(in,&lineno);
|
||||
tplt_skip_header(in);
|
||||
}else{
|
||||
tplt_xfer(lemp->name,in,out,&lineno);
|
||||
}
|
||||
@@ -4563,7 +4573,7 @@ void ReportTable(
|
||||
if( mhflag ){
|
||||
fprintf(out,"#if INTERFACE\n"); lineno++;
|
||||
}else{
|
||||
fprintf(out,"#ifndef %s%s\n", prefix, lemp->symbols[1]->name);
|
||||
fprintf(out,"#ifndef %s%s\n", prefix, lemp->symbols[1]->name); lineno++;
|
||||
}
|
||||
for(i=1; i<lemp->nterminal; i++){
|
||||
fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
|
||||
@@ -4612,25 +4622,33 @@ void ReportTable(
|
||||
fprintf(out,"#define %sARG_FETCH\n",name); lineno++;
|
||||
fprintf(out,"#define %sARG_STORE\n",name); lineno++;
|
||||
}
|
||||
fprintf(out, "#undef YYREALLOC\n"); lineno++;
|
||||
if( lemp->reallocFunc ){
|
||||
fprintf(out,"#define YYREALLOC %s\n", lemp->reallocFunc); lineno++;
|
||||
}else{
|
||||
fprintf(out,"#define YYREALLOC realloc\n"); lineno++;
|
||||
}
|
||||
fprintf(out, "#undef YYFREE\n"); lineno++;
|
||||
if( lemp->freeFunc ){
|
||||
fprintf(out,"#define YYFREE %s\n", lemp->freeFunc); lineno++;
|
||||
}else{
|
||||
fprintf(out,"#define YYFREE free\n"); lineno++;
|
||||
}
|
||||
fprintf(out, "#undef YYDYNSTACK\n"); lineno++;
|
||||
if( lemp->reallocFunc && lemp->freeFunc ){
|
||||
fprintf(out,"#define YYDYNSTACK 1\n"); lineno++;
|
||||
}else{
|
||||
fprintf(out,"#define YYDYNSTACK 0\n"); lineno++;
|
||||
}
|
||||
if( lemp->ctx && lemp->ctx[0] ){
|
||||
fprintf(out, "#undef YYSIZELIMIT\n"); lineno++;
|
||||
if( notnull(lemp->ctx) ){
|
||||
i = lemonStrlen(lemp->ctx);
|
||||
while( i>=1 && ISSPACE(lemp->ctx[i-1]) ) i--;
|
||||
while( i>=1 && (ISALNUM(lemp->ctx[i-1]) || lemp->ctx[i-1]=='_') ) i--;
|
||||
if( notnull(lemp->stackSizeLimit) ){
|
||||
fprintf(out,"#define YYSIZELIMIT %s\n", lemp->stackSizeLimit); lineno++;
|
||||
}
|
||||
fprintf(out,"#define %sCTX(P) ((P)->%s)\n",name,&lemp->ctx[i]); lineno++;
|
||||
fprintf(out,"#define %sCTX_SDECL %s;\n",name,lemp->ctx); lineno++;
|
||||
fprintf(out,"#define %sCTX_PDECL ,%s\n",name,lemp->ctx); lineno++;
|
||||
fprintf(out,"#define %sCTX_PARAM ,%s\n",name,&lemp->ctx[i]); lineno++;
|
||||
@@ -4639,6 +4657,7 @@ void ReportTable(
|
||||
fprintf(out,"#define %sCTX_STORE yypParser->%s=%s;\n",
|
||||
name,&lemp->ctx[i],&lemp->ctx[i]); lineno++;
|
||||
}else{
|
||||
fprintf(out,"#define %sCTX(P) 0\n",name); lineno++;
|
||||
fprintf(out,"#define %sCTX_SDECL\n",name); lineno++;
|
||||
fprintf(out,"#define %sCTX_PDECL\n",name); lineno++;
|
||||
fprintf(out,"#define %sCTX_PARAM\n",name); lineno++;
|
||||
@@ -4648,10 +4667,13 @@ void ReportTable(
|
||||
if( mhflag ){
|
||||
fprintf(out,"#endif\n"); lineno++;
|
||||
}
|
||||
fprintf(out, "#undef YYERRORSYMBOL\n"); lineno++;
|
||||
fprintf(out, "#undef YYERRSYMDT\n"); lineno++;
|
||||
if( lemp->errsym && lemp->errsym->useCnt ){
|
||||
fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++;
|
||||
fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++;
|
||||
}
|
||||
fprintf(out,"#undef YYFALLBACK\n"); lineno++;
|
||||
if( lemp->has_fallback ){
|
||||
fprintf(out,"#define YYFALLBACK 1\n"); lineno++;
|
||||
}
|
||||
@@ -5003,7 +5025,6 @@ void ReportTable(
|
||||
sp2->destLineno = -1; /* Avoid emitting this destructor again */
|
||||
}
|
||||
}
|
||||
|
||||
emit_destructor_code(out,lemp->symbols[i],lemp,&lineno);
|
||||
fprintf(out," break;\n"); lineno++;
|
||||
}
|
||||
@@ -5103,7 +5124,6 @@ void ReportTable(
|
||||
acttab_free(pActtab);
|
||||
fclose(in);
|
||||
fclose(out);
|
||||
if( sql ) fclose(sql);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+14
-3
@@ -299,15 +299,24 @@ static int yyGrowStack(yyParser *p){
|
||||
int newSize;
|
||||
int idx;
|
||||
yyStackEntry *pNew;
|
||||
#ifdef YYSIZELIMIT
|
||||
int nLimit = YYSIZELIMIT(ParseCTX(p));
|
||||
#endif
|
||||
|
||||
newSize = oldSize*2 + 100;
|
||||
#ifdef YYSIZELIMIT
|
||||
if( newSize>nLimit ){
|
||||
newSize = nLimit;
|
||||
if( newSize<=oldSize ) return 1;
|
||||
}
|
||||
#endif
|
||||
idx = (int)(p->yytos - p->yystack);
|
||||
if( p->yystack==p->yystk0 ){
|
||||
pNew = YYREALLOC(0, newSize*sizeof(pNew[0]));
|
||||
pNew = YYREALLOC(0, newSize*sizeof(pNew[0]), ParseCTX(p));
|
||||
if( pNew==0 ) return 1;
|
||||
memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0]));
|
||||
}else{
|
||||
pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]));
|
||||
pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]), ParseCTX(p));
|
||||
if( pNew==0 ) return 1;
|
||||
}
|
||||
p->yystack = pNew;
|
||||
@@ -459,7 +468,9 @@ void ParseFinalize(void *p){
|
||||
}
|
||||
|
||||
#if YYGROWABLESTACK
|
||||
if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack);
|
||||
if( pParser->yystack!=pParser->yystk0 ){
|
||||
YYFREE(pParser->yystack, ParseCTX(pParser));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/tclsh
|
||||
#
|
||||
# Use this script to combine multiple source code files into a single
|
||||
# file. Example:
|
||||
#
|
||||
# tclsh mkcombo.tcl file1.c file2.c file3.c -o file123.c
|
||||
#
|
||||
|
||||
set help {Usage: tclsh mkcombo.tcl [OPTIONS] [FILELIST]
|
||||
where OPTIONS is zero or more of the following with these effects:
|
||||
--linemacros=? => Emit #line directives into output or not. (? = 1 or 0)
|
||||
--o FILE => write to alternative output file named FILE
|
||||
--help => See this.
|
||||
}
|
||||
|
||||
set linemacros 0
|
||||
set fname {}
|
||||
set src [list]
|
||||
|
||||
|
||||
for {set i 0} {$i<[llength $argv]} {incr i} {
|
||||
set x [lindex $argv $i]
|
||||
if {[regexp {^-?-linemacros(?:=([01]))?$} $x ma ulm]} {
|
||||
if {$ulm == ""} {set ulm 1}
|
||||
set linemacros $ulm
|
||||
} elseif {[regexp {^-o$} $x]} {
|
||||
incr i
|
||||
if {$i==[llength $argv]} {
|
||||
error "No argument following $x"
|
||||
}
|
||||
set fname [lindex $argv $i]
|
||||
} elseif {[regexp {^-?-((help)|\?)$} $x]} {
|
||||
puts $help
|
||||
exit 0
|
||||
} elseif {[regexp {^-?-} $x]} {
|
||||
error "unknown command-line option: $x"
|
||||
} else {
|
||||
lappend src $x
|
||||
}
|
||||
}
|
||||
|
||||
# Open the output file and write a header comment at the beginning
|
||||
# of the file.
|
||||
#
|
||||
if {![info exists fname]} {
|
||||
set fname sqlite3.c
|
||||
if {$enable_recover} { set fname sqlite3r.c }
|
||||
}
|
||||
set out [open $fname wb]
|
||||
|
||||
# Return a string consisting of N "*" characters.
|
||||
#
|
||||
proc star N {
|
||||
set r {}
|
||||
for {set i 0} {$i<$N} {incr i} {append r *}
|
||||
return $r
|
||||
}
|
||||
|
||||
# Force the output to use unix line endings, even on Windows.
|
||||
fconfigure $out -translation binary
|
||||
puts $out "/[star 78]"
|
||||
puts $out {** The following is an amalgamation of these source code files:}
|
||||
puts $out {**}
|
||||
foreach s $src {
|
||||
regsub {^.*/(src|ext)/} $s {\1/} s2
|
||||
puts $out "** $s2"
|
||||
}
|
||||
puts $out {**}
|
||||
puts $out "[star 78]/"
|
||||
|
||||
# Insert a comment into the code
|
||||
#
|
||||
proc section_comment {text} {
|
||||
global out s78
|
||||
set n [string length $text]
|
||||
set nstar [expr {60 - $n}]
|
||||
puts $out "/************** $text [star $nstar]/"
|
||||
}
|
||||
|
||||
# Read the source file named $filename and write it into the
|
||||
# sqlite3.c output file. The only transformation is the trimming
|
||||
# of EOL whitespace.
|
||||
#
|
||||
proc copy_file_verbatim {filename} {
|
||||
global out
|
||||
set in [open $filename rb]
|
||||
set tail [file tail $filename]
|
||||
section_comment "Begin file $tail"
|
||||
while {![eof $in]} {
|
||||
set line [string trimright [gets $in]]
|
||||
puts $out $line
|
||||
}
|
||||
section_comment "End of $tail"
|
||||
}
|
||||
set taillist ""
|
||||
foreach file $src {
|
||||
copy_file_verbatim $file
|
||||
append taillist ", [file tail $file]"
|
||||
}
|
||||
|
||||
set taillist "End of the amalgamation of [string range $taillist 2 end]"
|
||||
set n [string length $taillist]
|
||||
set ns [expr {(75-$n)/2}]
|
||||
if {$ns<3} {set ns 3}
|
||||
puts $out "/[star $ns] $taillist [star $ns]/"
|
||||
close $out
|
||||
+95
-25
@@ -13,28 +13,90 @@ set topdir [file dir [file dir [file normal $argv0]]]
|
||||
set out stdout
|
||||
fconfigure stdout -translation binary
|
||||
if {[lindex $argv 0]!=""} {
|
||||
set out [open [lindex $argv 0] wb]
|
||||
set output_file [lindex $argv 0]
|
||||
file delete -force $output_file
|
||||
set out [open $output_file wb]
|
||||
} else {
|
||||
set output_file {}
|
||||
}
|
||||
puts $out {/* DO NOT EDIT!
|
||||
** This file is automatically generated by the script in the canonical
|
||||
** SQLite source tree at tool/mkshellc.tcl. That script combines source
|
||||
** code from various constituent source files of SQLite into this single
|
||||
** "shell.c" file used to implement the SQLite command-line shell.
|
||||
**
|
||||
** Most of the code found below comes from the "src/shell.c.in" file in
|
||||
** the canonical SQLite source tree. That main file contains "INCLUDE"
|
||||
** lines that specify other files in the canonical source tree that are
|
||||
** inserted to getnerate this complete program source file.
|
||||
**
|
||||
** The code from multiple files is combined into this single "shell.c"
|
||||
** source file to help make the command-line program easier to compile.
|
||||
**
|
||||
** To modify this program, get a copy of the canonical SQLite source tree,
|
||||
** edit the src/shell.c.in" and/or some of the other files that are included
|
||||
** by "src/shell.c.in", then rerun the tool/mkshellc.tcl script.
|
||||
*/}
|
||||
|
||||
############################## FIRST PASS ################################
|
||||
# Read through the shell.c.in source file to gather information. Do not
|
||||
# yet generate any code
|
||||
#
|
||||
set in [open $topdir/src/shell.c.in]
|
||||
fconfigure $in -translation binary
|
||||
set allSource(src/shell.c.in) 1
|
||||
set inUsage 0
|
||||
set dotcmd {}
|
||||
while {1} {
|
||||
set lx [gets $in]
|
||||
if {[eof $in]} break;
|
||||
if {[regexp {^INCLUDE } $lx]} {
|
||||
set cfile [lindex $lx 1]
|
||||
if {[string match ../* $cfile]} {
|
||||
set xfile [string range $cfile 3 end]
|
||||
} else {
|
||||
set xfile "src/$cfile"
|
||||
}
|
||||
set allSource($xfile) 1
|
||||
} elseif {[regexp {^\*\* USAGE:\s+([^\s]+)} $lx all dotcmd]} {
|
||||
set inUsage 1
|
||||
set details [string trim [string range $lx 2 end]]
|
||||
} elseif {$inUsage} {
|
||||
if {![regexp {^\*\*} $lx] || [regexp { DOT-COMMAND: } $lx]} {
|
||||
set inUsage 0
|
||||
set Usage($dotcmd) [string trim $details]
|
||||
} else {
|
||||
append details \n
|
||||
append details [string range [string trimright $lx] 3 end]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Generate dot-command usage text based on the data accumulated in
|
||||
# the Usage() array.
|
||||
#
|
||||
proc generate_usage {out} {
|
||||
global Usage
|
||||
puts $out "/**************************************************************"
|
||||
puts $out "** \"Usage\" help text automatically generated from comments */"
|
||||
puts $out "static const struct \173"
|
||||
puts $out " const char *zCmd; /* Name of the dot-command */"
|
||||
puts $out " const char *zUsage; /* Documentation */"
|
||||
puts $out "\175 aUsage\[\] = \173"
|
||||
foreach dotcmd [array names Usage] {
|
||||
puts $out " \173 \"$dotcmd\","
|
||||
foreach line [split $Usage($dotcmd) \n] {
|
||||
set x [string map [list \\ \\\\ \" \\\"] $line]
|
||||
puts $out "\"$x\\n\""
|
||||
}
|
||||
puts $out " \175,"
|
||||
}
|
||||
puts $out "\175;"
|
||||
}
|
||||
# generate_usage stderr
|
||||
|
||||
###### SECOND PASS #######
|
||||
# Make a second pass through shell.c.in to generate the the final
|
||||
# output, based on data gathered during the first pass.
|
||||
#
|
||||
|
||||
puts $out {/*
|
||||
** This is the amalgamated source code to the "sqlite3" or "sqlite3.exe"
|
||||
** command-line shell (CLI) for SQLite. This file is automatically
|
||||
** generated by the tool/mkshellc.tcl script from the following sources:
|
||||
**}
|
||||
foreach fn [lsort [array names allSource]] {
|
||||
puts $out "** $fn"
|
||||
}
|
||||
puts $out {**
|
||||
** To modify this program, get a copy of the canonical SQLite source tree,
|
||||
** edit the src/shell.c.in file and/or some of the other files that are
|
||||
** listed above, then rerun the rerun "make shell.c".
|
||||
*/}
|
||||
seek $in 0 start
|
||||
puts $out "/************************* Begin src/shell.c.in ******************/"
|
||||
proc omit_redundant_typedefs {line} {
|
||||
global typedef_seen
|
||||
if {[regexp {^typedef .* ([a-zA-Z0-9_]+);} $line all typename]} {
|
||||
@@ -53,9 +115,14 @@ while {1} {
|
||||
incr iLine
|
||||
if {[regexp {^INCLUDE } $lx]} {
|
||||
set cfile [lindex $lx 1]
|
||||
puts $out "/************************* Begin $cfile ******************/"
|
||||
# puts $out "#line 1 \"$cfile\""
|
||||
set in2 [open $topdir/src/$cfile]
|
||||
if {[string match ../* $cfile]} {
|
||||
set xfile [string range $cfile 3 end]
|
||||
} else {
|
||||
set xfile "src/$cfile"
|
||||
}
|
||||
puts $out "/************************* Begin $xfile ******************/"
|
||||
# puts $out "#line 1 \"$xfile\""
|
||||
set in2 [open $topdir/$xfile]
|
||||
fconfigure $in2 -translation binary
|
||||
while {![eof $in2]} {
|
||||
set lx [omit_redundant_typedefs [gets $in2]]
|
||||
@@ -69,11 +136,14 @@ while {1} {
|
||||
puts $out $lx
|
||||
}
|
||||
close $in2
|
||||
puts $out "/************************* End $cfile ********************/"
|
||||
puts $out "/************************* End $xfile ********************/"
|
||||
# puts $out "#line [expr $iLine+1] \"shell.c.in\""
|
||||
continue
|
||||
} elseif {[regexp {^INSERT-USAGE-TEXT-HERE} $lx]} {
|
||||
generate_usage $out
|
||||
} else {
|
||||
puts $out $lx
|
||||
}
|
||||
puts $out $lx
|
||||
}
|
||||
puts $out "/************************* End src/shell.c.in ******************/"
|
||||
close $in
|
||||
close $out
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ INCLUDE $ROOT/ext/misc/appendvfs.c
|
||||
INCLUDE $ROOT/ext/misc/zipfile.c
|
||||
INCLUDE $ROOT/ext/misc/sqlar.c
|
||||
#endif
|
||||
INCLUDE $ROOT/src/tclsqlite.c
|
||||
INCLUDE tclsqlite-ex.c
|
||||
|
||||
const char *sqlite3_tclapp_init_proc(Tcl_Interp *interp){
|
||||
(void)interp;
|
||||
|
||||
Reference in New Issue
Block a user