/ Check-in [d65d1f29]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Improved ability to generate stand-alone program using TCL and SQLite by compiling with -DTCLSH_INIT_PROC=name to cause the TCL interpreter to be initialized using procedure name(). Both sqlite3_analyzer and testfixture are now built this way.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: d65d1f297ddb07b799ff5b2e560575fc59a6fa74c752269cc85ab84348fb7da4
User & Date: drh 2017-10-13 20:14:06
Context
2017-10-14
19:54
Updates to the Makefiles for MSVC. check-in: ac8786f3 user: mistachkin tags: trunk
2017-10-13
20:14
Improved ability to generate stand-alone program using TCL and SQLite by compiling with -DTCLSH_INIT_PROC=name to cause the TCL interpreter to be initialized using procedure name(). Both sqlite3_analyzer and testfixture are now built this way. check-in: d65d1f29 user: drh tags: trunk
18:58
Add the tcl/mkccode.tcl script used to construct a single C-language source fiel for programs that combine C-code, SQLite, and TCL. Use this script to construct the sqlite3_analyzer program. check-in: 298a3fdd user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Makefile.in.

   943    943   whereexpr.lo:	$(TOP)/src/whereexpr.c $(HDR)
   944    944   	$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/whereexpr.c
   945    945   
   946    946   tclsqlite.lo:	$(TOP)/src/tclsqlite.c $(HDR)
   947    947   	$(LTCOMPILE) -DUSE_TCL_STUBS=1 -c $(TOP)/src/tclsqlite.c
   948    948   
   949    949   tclsqlite-shell.lo:	$(TOP)/src/tclsqlite.c $(HDR)
   950         -	$(LTCOMPILE) -DTCLSH=1 -o $@ -c $(TOP)/src/tclsqlite.c
          950  +	$(LTCOMPILE) -DTCLSH -o $@ -c $(TOP)/src/tclsqlite.c
   951    951   
   952    952   tclsqlite-stubs.lo:	$(TOP)/src/tclsqlite.c $(HDR)
   953    953   	$(LTCOMPILE) -DUSE_TCL_STUBS=1 -o $@ -c $(TOP)/src/tclsqlite.c
   954    954   
   955    955   tclsqlite3$(TEXE):	tclsqlite-shell.lo libsqlite3.la
   956    956   	$(LTLINK) -o $@ tclsqlite-shell.lo \
   957    957   		 libsqlite3.la $(LIBTCL)
................................................................................
  1109   1109   # Rules to build the 'testfixture' application.
  1110   1110   #
  1111   1111   # If using the amalgamation, use sqlite3.c directly to build the test
  1112   1112   # fixture.  Otherwise link against libsqlite3.la.  (This distinction is
  1113   1113   # necessary because the test fixture requires non-API symbols which are
  1114   1114   # hidden when the library is built via the amalgamation).
  1115   1115   #
  1116         -TESTFIXTURE_FLAGS  = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
         1116  +TESTFIXTURE_FLAGS  = -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
         1117  +TESTFIXTURE_FLAGS += -DTCLSH_INIT_PROC=sqlite3TestInit
  1117   1118   TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE 
  1118   1119   TESTFIXTURE_FLAGS += -DBUILD_sqlite
  1119   1120   TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
  1120   1121   TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024
  1121   1122   TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB
  1122   1123   TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB
  1123   1124   

Changes to Makefile.msc.

  1930   1930   whereexpr.lo:	$(TOP)\src\whereexpr.c $(HDR)
  1931   1931   	$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\whereexpr.c
  1932   1932   
  1933   1933   tclsqlite.lo:	$(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
  1934   1934   	$(LTCOMPILE) $(NO_WARN) -DUSE_TCL_STUBS=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
  1935   1935   
  1936   1936   tclsqlite-shell.lo:	$(TOP)\src\tclsqlite.c $(HDR) $(SQLITE_TCL_DEP)
  1937         -	$(LTCOMPILE) $(NO_WARN) -DTCLSH=1 -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
         1937  +	$(LTCOMPILE) $(NO_WARN) -DTCLSH -DBUILD_sqlite -I$(TCLINCDIR) -c $(TOP)\src\tclsqlite.c
  1938   1938   
  1939   1939   tclsqlite3.exe:	tclsqlite-shell.lo $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
  1940   1940   	$(LTLINK) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite-shell.lo $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
  1941   1941   
  1942   1942   # Rules to build opcodes.c and opcodes.h
  1943   1943   #
  1944   1944   opcodes.c:	opcodes.h $(TOP)\tool\mkopcodec.tcl
................................................................................
  2101   2101   # Rules to build the 'testfixture' application.
  2102   2102   #
  2103   2103   # If using the amalgamation, use sqlite3.c directly to build the test
  2104   2104   # fixture.  Otherwise link against libsqlite3.lib.  (This distinction is
  2105   2105   # necessary because the test fixture requires non-API symbols which are
  2106   2106   # hidden when the library is built via the amalgamation).
  2107   2107   #
  2108         -TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
         2108  +TESTFIXTURE_FLAGS = -DTCLSH_INIT_PROC=sqlite3TestInit -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
  2109   2109   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE=""
  2110   2110   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN)
  2111   2111   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
  2112   2112   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024
  2113   2113   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB
  2114   2114   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB
  2115   2115   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS)

Changes to main.mk.

   773    773   
   774    774   sqlite3rbu.o:	$(TOP)/ext/rbu/sqlite3rbu.c $(HDR) $(EXTHDR)
   775    775   	$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rbu/sqlite3rbu.c
   776    776   
   777    777   # Rules for building test programs and for running tests
   778    778   #
   779    779   tclsqlite3:	$(TOP)/src/tclsqlite.c libsqlite3.a
   780         -	$(TCCX) $(TCL_FLAGS) -DTCLSH=1 -o tclsqlite3 \
          780  +	$(TCCX) $(TCL_FLAGS) -DTCLSH -o tclsqlite3 \
   781    781   		$(TOP)/src/tclsqlite.c libsqlite3.a $(LIBTCL) $(THREADLIB)
   782    782   
   783    783   sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/sqlite3_analyzer.c.in $(TOP)/tool/mkccode.tcl
   784    784   	tclsh $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in >sqlite3_analyzer.c
   785    785   
   786    786   sqlite3_analyzer$(EXE): sqlite3_analyzer.c
   787    787   	$(TCCX) $(TCL_FLAGS) sqlite3_analyzer.c -o $@ $(LIBTCL) $(THREADLIB) 
................................................................................
   794    794   #
   795    795   TESTFIXTURE_FLAGS  = -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
   796    796   TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
   797    797   TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
   798    798   TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024
   799    799   TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB
   800    800   TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB
          801  +TESTFIXTURE_FLAGS += -DTCLSH_INIT_PROC=sqlite3TestInit
   801    802   
   802    803   testfixture$(EXE): $(TESTSRC2) libsqlite3.a $(TESTSRC) $(TOP)/src/tclsqlite.c
   803         -	$(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS)                  \
          804  +	$(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS)                            \
   804    805   		$(TESTSRC) $(TESTSRC2) $(TOP)/src/tclsqlite.c                \
   805    806   		-o testfixture$(EXE) $(LIBTCL) libsqlite3.a $(THREADLIB)
   806    807   
   807    808   amalgamation-testfixture$(EXE): sqlite3.c $(TESTSRC) $(TOP)/src/tclsqlite.c  \
   808    809   				$(TOP)/ext/session/test_session.c
   809         -	$(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS)                  \
          810  +	$(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS)                            \
   810    811   		$(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c                  \
   811    812   		$(TOP)/ext/session/test_session.c                            \
   812    813   		-o testfixture$(EXE) $(LIBTCL) $(THREADLIB)
   813    814   
   814    815   fts3-testfixture$(EXE): sqlite3.c fts3amal.c $(TESTSRC) $(TOP)/src/tclsqlite.c
   815         -	$(TCCX) $(TCL_FLAGS) -DTCLSH=1 $(TESTFIXTURE_FLAGS)                  \
          816  +	$(TCCX) $(TCL_FLAGS) $(TESTFIXTURE_FLAGS)                            \
   816    817   	-DSQLITE_ENABLE_FTS3=1                                               \
   817    818   		$(TESTSRC) $(TOP)/src/tclsqlite.c sqlite3.c fts3amal.c       \
   818    819   		-o testfixture$(EXE) $(LIBTCL) $(THREADLIB)
   819    820   
   820    821   fulltest:	$(TESTPROGS) fuzztest
   821    822   	./testfixture$(EXE) $(TOP)/test/all.test $(TESTOPTS)
   822    823   

Changes to src/tclsqlite.c.

    10     10   **
    11     11   *************************************************************************
    12     12   ** A TCL Interface to SQLite.  Append this file to sqlite3.c and
    13     13   ** compile the whole thing to build a TCL-enabled version of SQLite.
    14     14   **
    15     15   ** Compile-time options:
    16     16   **
    17         -**  -DTCLSH=1             Add a "main()" routine that works as a tclsh.
           17  +**  -DTCLSH         Add a "main()" routine that works as a tclsh.
    18     18   **
    19         -**  -DSQLITE_TCLMD5       When used in conjuction with -DTCLSH=1, add
    20         -**                        four new commands to the TCL interpreter for
    21         -**                        generating MD5 checksums:  md5, md5file,
    22         -**                        md5-10x8, and md5file-10x8.
           19  +**  -DTCLSH_INIT_PROC=name
    23     20   **
    24         -**  -DSQLITE_TEST         When used in conjuction with -DTCLSH=1, add
    25         -**                        hundreds of new commands used for testing
    26         -**                        SQLite.  This option implies -DSQLITE_TCLMD5.
           21  +**                  Invoke name(interp) to initialize the Tcl interpreter.
           22  +**                  If name(interp) returns a non-NULL string, then run
           23  +**                  that string as a Tcl script to launch the application.
           24  +**                  If name(interp) returns NULL, then run the regular
           25  +**                  tclsh-emulator code.
    27     26   */
           27  +#ifdef TCLSH_INIT_PROC
           28  +# define TCLSH 1
           29  +#endif
    28     30   
    29     31   /*
    30     32   ** If requested, include the SQLite compiler options file for MSVC.
    31     33   */
    32     34   #if defined(INCLUDE_MSVC_H)
    33     35   # include "msvc.h"
    34     36   #endif
................................................................................
  3578   3580   int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
  3579   3581   int Tclsqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
  3580   3582   int Sqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
  3581   3583   int Tclsqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
  3582   3584   #endif
  3583   3585   
  3584   3586   /*
  3585         -** If the TCLSH macro is defined to be either 1 or 2, then a main()
  3586         -** routine is inserted that starts up a Tcl interpreter.  When TCLSH==1
  3587         -** the interpreter works like an ordinary tclsh.  When TCLSH==2 then the
  3588         -** startup script is supplied by an routine named "tclsh_main_loop()"
  3589         -** that must be linked separately.  The TCLSH==2 technique is used to
  3590         -** generate stand-alone executables based on TCL, such as 
  3591         -** sqlite3_analyzer.exe.
         3587  +** If the TCLSH macro is defined, add code to make a stand-alone program.
  3592   3588   */
  3593         -#ifdef TCLSH
         3589  +#if defined(TCLSH)
  3594   3590   
  3595         -/*
  3596         -** If the macro TCLSH is one, then put in code this for the
  3597         -** "main" routine that will initialize Tcl and take input from
  3598         -** standard input, or if a file is named on the command line
  3599         -** the TCL interpreter reads and evaluates that file.
         3591  +/* This is the main routine for an ordinary TCL shell.  If there are
         3592  +** are arguments, run the first argument as a script.  Otherwise,
         3593  +** read TCL commands from standard input
  3600   3594   */
  3601         -#if TCLSH==1
  3602   3595   static const char *tclsh_main_loop(void){
  3603   3596     static const char zMainloop[] =
  3604         -    "set line {}\n"
  3605         -    "while {![eof stdin]} {\n"
  3606         -      "if {$line!=\"\"} {\n"
  3607         -        "puts -nonewline \"> \"\n"
  3608         -      "} else {\n"
  3609         -        "puts -nonewline \"% \"\n"
  3610         -      "}\n"
  3611         -      "flush stdout\n"
  3612         -      "append line [gets stdin]\n"
  3613         -      "if {[info complete $line]} {\n"
  3614         -        "if {[catch {uplevel #0 $line} result]} {\n"
  3615         -          "puts stderr \"Error: $result\"\n"
  3616         -        "} elseif {$result!=\"\"} {\n"
  3617         -          "puts $result\n"
         3597  +    "if {[llength $argv]>=1} {\n"
         3598  +      "set argv0 [lindex $argv 0]\n"
         3599  +      "set argv [lrange $argv 1 end]\n"
         3600  +      "source $argv0\n"
         3601  +    "} else {\n"
         3602  +      "set line {}\n"
         3603  +      "while {![eof stdin]} {\n"
         3604  +        "if {$line!=\"\"} {\n"
         3605  +          "puts -nonewline \"> \"\n"
         3606  +        "} else {\n"
         3607  +          "puts -nonewline \"% \"\n"
  3618   3608           "}\n"
  3619         -        "set line {}\n"
  3620         -      "} else {\n"
  3621         -        "append line \\n\n"
         3609  +        "flush stdout\n"
         3610  +        "append line [gets stdin]\n"
         3611  +        "if {[info complete $line]} {\n"
         3612  +          "if {[catch {uplevel #0 $line} result]} {\n"
         3613  +            "puts stderr \"Error: $result\"\n"
         3614  +          "} elseif {$result!=\"\"} {\n"
         3615  +            "puts $result\n"
         3616  +          "}\n"
         3617  +          "set line {}\n"
         3618  +        "} else {\n"
         3619  +          "append line \\n\n"
         3620  +        "}\n"
  3622   3621         "}\n"
  3623   3622       "}\n"
  3624   3623     ;
  3625   3624     return zMainloop;
  3626   3625   }
  3627         -#endif
  3628         -#if TCLSH==2
  3629         -static const char *tclsh_main_loop(void);
  3630         -#endif
  3631   3626   
  3632   3627   #define TCLSH_MAIN main   /* Needed to fake out mktclapp */
  3633   3628   int SQLITE_CDECL TCLSH_MAIN(int argc, char **argv){
  3634   3629     Tcl_Interp *interp;
         3630  +  int i;
         3631  +  const char *zScript = 0;
         3632  +  char zArgc[32];
         3633  +#if defined(TCLSH_INIT_PROC)
         3634  +  extern const char *TCLSH_INIT_PROC(Tcl_Interp*);
         3635  +#endif
  3635   3636   
  3636   3637   #if !defined(_WIN32_WCE)
  3637   3638     if( getenv("BREAK") ){
  3638   3639       fprintf(stderr,
  3639   3640           "attach debugger to process %d and press any key to continue.\n",
  3640   3641           GETPID());
  3641   3642       fgetc(stdin);
................................................................................
  3646   3647     ** test that sqlite3_shutdown() can be safely called by a process before
  3647   3648     ** sqlite3_initialize() is. */
  3648   3649     sqlite3_shutdown();
  3649   3650   
  3650   3651     Tcl_FindExecutable(argv[0]);
  3651   3652     Tcl_SetSystemEncoding(NULL, "utf-8");
  3652   3653     interp = Tcl_CreateInterp();
  3653         -
  3654         -#if TCLSH==2
  3655         -  sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
  3656         -#endif
         3654  +  Sqlite3_Init(interp);
  3657   3655   
  3658         -  /* Add extensions */
  3659         -#if !defined(SQLITE_TEST)
  3660         -  /* Normally we only initialize the TCL extension */
  3661         -  Sqlite3_Init(interp);
  3662         -#else
  3663         -  /* For testing, do lots of extra initialization */
  3664         -  {
  3665         -    extern void sqlite3InitTclTestLogic(Tcl_Interp*);
  3666         -    sqlite3InitTclTestLogic(interp);
         3656  +  sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-1);
         3657  +  Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY);
         3658  +  Tcl_SetVar(interp,"argv0",argv[0],TCL_GLOBAL_ONLY);
         3659  +  Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY);
         3660  +  for(i=1; i<argc; i++){
         3661  +    Tcl_SetVar(interp, "argv", argv[i],
         3662  +        TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE);
         3663  +  }
         3664  +#if defined(TCLSH_INIT_PROC)
         3665  +  zScript = TCLSH_INIT_PROC(interp);
         3666  +#endif
         3667  +  if( zScript==0 ){
         3668  +    zScript = tclsh_main_loop();
  3667   3669     }
  3668         -#endif /* SQLITE_TEST */
  3669         -  if( argc>=2 ){
  3670         -    int i;
  3671         -    char zArgc[32];
  3672         -    sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-(3-TCLSH));
  3673         -    Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY);
  3674         -    Tcl_SetVar(interp,"argv0",argv[1],TCL_GLOBAL_ONLY);
  3675         -    Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY);
  3676         -    for(i=3-TCLSH; i<argc; i++){
  3677         -      Tcl_SetVar(interp, "argv", argv[i],
  3678         -          TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE);
  3679         -    }
  3680         -    if( TCLSH==1 && Tcl_EvalFile(interp, argv[1])!=TCL_OK ){
  3681         -      const char *zInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
  3682         -      if( zInfo==0 ) zInfo = Tcl_GetStringResult(interp);
  3683         -      fprintf(stderr,"%s: %s\n", *argv, zInfo);
  3684         -      return 1;
  3685         -    }
  3686         -  }
  3687         -  if( TCLSH==2 || argc<=1 ){
  3688         -    Tcl_GlobalEval(interp, tclsh_main_loop());
         3670  +  if( Tcl_GlobalEval(interp, zScript)!=TCL_OK ){
         3671  +    const char *zInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
         3672  +    if( zInfo==0 ) zInfo = Tcl_GetStringResult(interp);
         3673  +    fprintf(stderr,"%s: %s\n", *argv, zInfo);
         3674  +    return 1;
  3689   3675     }
  3690   3676     return 0;
  3691   3677   }
  3692   3678   #endif /* TCLSH */

Changes to src/test_tclsh.c.

    51     51   **   * the [sqlite3] extension itself,
    52     52   **
    53     53   **   * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and
    54     54   **
    55     55   **   * If SQLITE_TEST is set, the various test interfaces used by the Tcl
    56     56   **     test suite.
    57     57   */
    58         -void sqlite3InitTclTestLogic(Tcl_Interp *interp){
           58  +const char *sqlite3TestInit(Tcl_Interp *interp){
           59  +  extern int Sqlite3_Init(Tcl_Interp*);
           60  +  extern int Sqliteconfig_Init(Tcl_Interp*);
           61  +  extern int Sqlitetest1_Init(Tcl_Interp*);
           62  +  extern int Sqlitetest2_Init(Tcl_Interp*);
           63  +  extern int Sqlitetest3_Init(Tcl_Interp*);
           64  +  extern int Sqlitetest4_Init(Tcl_Interp*);
           65  +  extern int Sqlitetest5_Init(Tcl_Interp*);
           66  +  extern int Sqlitetest6_Init(Tcl_Interp*);
           67  +  extern int Sqlitetest7_Init(Tcl_Interp*);
           68  +  extern int Sqlitetest8_Init(Tcl_Interp*);
           69  +  extern int Sqlitetest9_Init(Tcl_Interp*);
           70  +  extern int Sqlitetestasync_Init(Tcl_Interp*);
           71  +  extern int Sqlitetest_autoext_Init(Tcl_Interp*);
           72  +  extern int Sqlitetest_blob_Init(Tcl_Interp*);
           73  +  extern int Sqlitetest_demovfs_Init(Tcl_Interp *);
           74  +  extern int Sqlitetest_func_Init(Tcl_Interp*);
           75  +  extern int Sqlitetest_hexio_Init(Tcl_Interp*);
           76  +  extern int Sqlitetest_init_Init(Tcl_Interp*);
           77  +  extern int Sqlitetest_malloc_Init(Tcl_Interp*);
           78  +  extern int Sqlitetest_mutex_Init(Tcl_Interp*);
           79  +  extern int Sqlitetestschema_Init(Tcl_Interp*);
           80  +  extern int Sqlitetestsse_Init(Tcl_Interp*);
           81  +  extern int Sqlitetesttclvar_Init(Tcl_Interp*);
           82  +  extern int Sqlitetestfs_Init(Tcl_Interp*);
           83  +  extern int SqlitetestThread_Init(Tcl_Interp*);
           84  +  extern int SqlitetestOnefile_Init();
           85  +  extern int SqlitetestOsinst_Init(Tcl_Interp*);
           86  +  extern int Sqlitetestbackup_Init(Tcl_Interp*);
           87  +  extern int Sqlitetestintarray_Init(Tcl_Interp*);
           88  +  extern int Sqlitetestvfs_Init(Tcl_Interp *);
           89  +  extern int Sqlitetestrtree_Init(Tcl_Interp*);
           90  +  extern int Sqlitequota_Init(Tcl_Interp*);
           91  +  extern int Sqlitemultiplex_Init(Tcl_Interp*);
           92  +  extern int SqliteSuperlock_Init(Tcl_Interp*);
           93  +  extern int SqlitetestSyscall_Init(Tcl_Interp*);
           94  +#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
           95  +  extern int TestSession_Init(Tcl_Interp*);
           96  +#endif
           97  +  extern int Md5_Init(Tcl_Interp*);
           98  +  extern int Fts5tcl_Init(Tcl_Interp *);
           99  +  extern int SqliteRbu_Init(Tcl_Interp*);
          100  +  extern int Sqlitetesttcl_Init(Tcl_Interp*);
          101  +#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
          102  +  extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
          103  +#endif
          104  +#ifdef SQLITE_ENABLE_ZIPVFS
          105  +  extern int Zipvfs_Init(Tcl_Interp*);
          106  +#endif
          107  +  Tcl_CmdInfo cmdInfo;
          108  +
    59    109     /* Since the primary use case for this binary is testing of SQLite,
    60    110     ** be sure to generate core files if we crash */
    61    111   #if defined(unix)
    62    112     { struct rlimit x;
    63    113       getrlimit(RLIMIT_CORE, &x);
    64    114       x.rlim_cur = x.rlim_max;
    65    115       setrlimit(RLIMIT_CORE, &x);
    66    116     }
    67    117   #endif /* unix */
    68    118   
    69         -  {
    70         -    extern int Sqlite3_Init(Tcl_Interp*);
    71         -    extern int Sqliteconfig_Init(Tcl_Interp*);
    72         -    extern int Sqlitetest1_Init(Tcl_Interp*);
    73         -    extern int Sqlitetest2_Init(Tcl_Interp*);
    74         -    extern int Sqlitetest3_Init(Tcl_Interp*);
    75         -    extern int Sqlitetest4_Init(Tcl_Interp*);
    76         -    extern int Sqlitetest5_Init(Tcl_Interp*);
    77         -    extern int Sqlitetest6_Init(Tcl_Interp*);
    78         -    extern int Sqlitetest7_Init(Tcl_Interp*);
    79         -    extern int Sqlitetest8_Init(Tcl_Interp*);
    80         -    extern int Sqlitetest9_Init(Tcl_Interp*);
    81         -    extern int Sqlitetestasync_Init(Tcl_Interp*);
    82         -    extern int Sqlitetest_autoext_Init(Tcl_Interp*);
    83         -    extern int Sqlitetest_blob_Init(Tcl_Interp*);
    84         -    extern int Sqlitetest_demovfs_Init(Tcl_Interp *);
    85         -    extern int Sqlitetest_func_Init(Tcl_Interp*);
    86         -    extern int Sqlitetest_hexio_Init(Tcl_Interp*);
    87         -    extern int Sqlitetest_init_Init(Tcl_Interp*);
    88         -    extern int Sqlitetest_malloc_Init(Tcl_Interp*);
    89         -    extern int Sqlitetest_mutex_Init(Tcl_Interp*);
    90         -    extern int Sqlitetestschema_Init(Tcl_Interp*);
    91         -    extern int Sqlitetestsse_Init(Tcl_Interp*);
    92         -    extern int Sqlitetesttclvar_Init(Tcl_Interp*);
    93         -    extern int Sqlitetestfs_Init(Tcl_Interp*);
    94         -    extern int SqlitetestThread_Init(Tcl_Interp*);
    95         -    extern int SqlitetestOnefile_Init();
    96         -    extern int SqlitetestOsinst_Init(Tcl_Interp*);
    97         -    extern int Sqlitetestbackup_Init(Tcl_Interp*);
    98         -    extern int Sqlitetestintarray_Init(Tcl_Interp*);
    99         -    extern int Sqlitetestvfs_Init(Tcl_Interp *);
   100         -    extern int Sqlitetestrtree_Init(Tcl_Interp*);
   101         -    extern int Sqlitequota_Init(Tcl_Interp*);
   102         -    extern int Sqlitemultiplex_Init(Tcl_Interp*);
   103         -    extern int SqliteSuperlock_Init(Tcl_Interp*);
   104         -    extern int SqlitetestSyscall_Init(Tcl_Interp*);
          119  +  if( Tcl_GetCommandInfo(interp, "sqlite3", &cmdInfo)==0 ){
          120  +    Sqlite3_Init(interp);
          121  +  }
          122  +#ifdef SQLITE_ENABLE_ZIPVFS
          123  +  Zipvfs_Init(interp);
          124  +#endif
          125  +  Md5_Init(interp);
          126  +  Sqliteconfig_Init(interp);
          127  +  Sqlitetest1_Init(interp);
          128  +  Sqlitetest2_Init(interp);
          129  +  Sqlitetest3_Init(interp);
          130  +  Sqlitetest4_Init(interp);
          131  +  Sqlitetest5_Init(interp);
          132  +  Sqlitetest6_Init(interp);
          133  +  Sqlitetest7_Init(interp);
          134  +  Sqlitetest8_Init(interp);
          135  +  Sqlitetest9_Init(interp);
          136  +  Sqlitetestasync_Init(interp);
          137  +  Sqlitetest_autoext_Init(interp);
          138  +  Sqlitetest_blob_Init(interp);
          139  +  Sqlitetest_demovfs_Init(interp);
          140  +  Sqlitetest_func_Init(interp);
          141  +  Sqlitetest_hexio_Init(interp);
          142  +  Sqlitetest_init_Init(interp);
          143  +  Sqlitetest_malloc_Init(interp);
          144  +  Sqlitetest_mutex_Init(interp);
          145  +  Sqlitetestschema_Init(interp);
          146  +  Sqlitetesttclvar_Init(interp);
          147  +  Sqlitetestfs_Init(interp);
          148  +  SqlitetestThread_Init(interp);
          149  +  SqlitetestOnefile_Init();
          150  +  SqlitetestOsinst_Init(interp);
          151  +  Sqlitetestbackup_Init(interp);
          152  +  Sqlitetestintarray_Init(interp);
          153  +  Sqlitetestvfs_Init(interp);
          154  +  Sqlitetestrtree_Init(interp);
          155  +  Sqlitequota_Init(interp);
          156  +  Sqlitemultiplex_Init(interp);
          157  +  SqliteSuperlock_Init(interp);
          158  +  SqlitetestSyscall_Init(interp);
   105    159   #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
   106         -    extern int TestSession_Init(Tcl_Interp*);
          160  +  TestSession_Init(interp);
   107    161   #endif
   108         -    extern int Md5_Init(Tcl_Interp*);
   109         -    extern int Fts5tcl_Init(Tcl_Interp *);
   110         -    extern int SqliteRbu_Init(Tcl_Interp*);
   111         -    extern int Sqlitetesttcl_Init(Tcl_Interp*);
          162  +  Fts5tcl_Init(interp);
          163  +  SqliteRbu_Init(interp);
          164  +  Sqlitetesttcl_Init(interp);
          165  +
   112    166   #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
   113         -    extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
   114         -#endif
   115         -#ifdef SQLITE_ENABLE_ZIPVFS
   116         -    extern int Zipvfs_Init(Tcl_Interp*);
          167  +  Sqlitetestfts3_Init(interp);
   117    168   #endif
   118    169   
   119         -    Sqlite3_Init(interp);
   120         -#ifdef SQLITE_ENABLE_ZIPVFS
   121         -    Zipvfs_Init(interp);
   122         -#endif
   123         -    Md5_Init(interp);
   124         -    Sqliteconfig_Init(interp);
   125         -    Sqlitetest1_Init(interp);
   126         -    Sqlitetest2_Init(interp);
   127         -    Sqlitetest3_Init(interp);
   128         -    Sqlitetest4_Init(interp);
   129         -    Sqlitetest5_Init(interp);
   130         -    Sqlitetest6_Init(interp);
   131         -    Sqlitetest7_Init(interp);
   132         -    Sqlitetest8_Init(interp);
   133         -    Sqlitetest9_Init(interp);
   134         -    Sqlitetestasync_Init(interp);
   135         -    Sqlitetest_autoext_Init(interp);
   136         -    Sqlitetest_blob_Init(interp);
   137         -    Sqlitetest_demovfs_Init(interp);
   138         -    Sqlitetest_func_Init(interp);
   139         -    Sqlitetest_hexio_Init(interp);
   140         -    Sqlitetest_init_Init(interp);
   141         -    Sqlitetest_malloc_Init(interp);
   142         -    Sqlitetest_mutex_Init(interp);
   143         -    Sqlitetestschema_Init(interp);
   144         -    Sqlitetesttclvar_Init(interp);
   145         -    Sqlitetestfs_Init(interp);
   146         -    SqlitetestThread_Init(interp);
   147         -    SqlitetestOnefile_Init();
   148         -    SqlitetestOsinst_Init(interp);
   149         -    Sqlitetestbackup_Init(interp);
   150         -    Sqlitetestintarray_Init(interp);
   151         -    Sqlitetestvfs_Init(interp);
   152         -    Sqlitetestrtree_Init(interp);
   153         -    Sqlitequota_Init(interp);
   154         -    Sqlitemultiplex_Init(interp);
   155         -    SqliteSuperlock_Init(interp);
   156         -    SqlitetestSyscall_Init(interp);
   157         -#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
   158         -    TestSession_Init(interp);
   159         -#endif
   160         -    Fts5tcl_Init(interp);
   161         -    SqliteRbu_Init(interp);
   162         -    Sqlitetesttcl_Init(interp);
   163         -
   164         -#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
   165         -    Sqlitetestfts3_Init(interp);
   166         -#endif
   167         -
   168         -    Tcl_CreateObjCommand(
   169         -        interp, "load_testfixture_extensions", load_testfixture_extensions,0,0
   170         -    );
   171         -  }
          170  +  Tcl_CreateObjCommand(
          171  +      interp, "load_testfixture_extensions", load_testfixture_extensions,0,0
          172  +  );
          173  +  return 0;
   172    174   }
   173    175   
   174    176   /* tclcmd:   load_testfixture_extensions
   175    177   */
   176    178   static int SQLITE_TCLAPI load_testfixture_extensions(
   177    179     ClientData cd,
   178    180     Tcl_Interp *interp,
................................................................................
   187    189     }
   188    190   
   189    191     slave = Tcl_GetSlave(interp, Tcl_GetString(objv[1]));
   190    192     if( !slave ){
   191    193       return TCL_ERROR;
   192    194     }
   193    195   
   194         -  sqlite3InitTclTestLogic(slave);
          196  +  (void)sqlite3TestInit(slave);
   195    197     return TCL_OK;
   196    198   }

Changes to tool/sqlite3_analyzer.c.in.

     1      1   /*
     2      2   ** Read an SQLite database file and analyze its space utilization.  Generate
     3      3   ** text on standard output.
     4      4   */
     5         -#define TCLSH 2
            5  +#define TCLSH_INIT_PROC sqlite3_analyzer_init_proc
     6      6   #define SQLITE_ENABLE_DBSTAT_VTAB 1
     7      7   #undef SQLITE_THREADSAFE
     8      8   #define SQLITE_THREADSAFE 0
     9      9   #undef SQLITE_ENABLE_COLUMN_METADATA
    10     10   #define SQLITE_OMIT_DECLTYPE 1
    11     11   #define SQLITE_OMIT_DEPRECATED 1
    12     12   #define SQLITE_OMIT_PROGRESS_CALLBACK 1
................................................................................
    13     13   #define SQLITE_OMIT_SHARED_CACHE 1
    14     14   #define SQLITE_DEFAULT_MEMSTATUS 0
    15     15   #define SQLITE_MAX_EXPR_DEPTH 0
    16     16   #define SQLITE_OMIT_LOAD_EXTENSION 1
    17     17   INCLUDE sqlite3.c
    18     18   INCLUDE $ROOT/src/tclsqlite.c
    19     19   
    20         -static const char *tclsh_main_loop(void){
    21         -return
           20  +const char *sqlite3_analyzer_init_proc(Tcl_Interp *interp){
           21  +  (void)interp;
           22  +  return
    22     23   BEGIN_STRING
    23         -# Run this TCL script using "testfixture" in order get a report that shows
    24         -# how much disk space is used by a particular data to actually store data
    25         -# versus how much space is unused.
    26         -#
    27         -
    28         -if {[catch {
    29         -
    30         -# Argument $tname is the name of a table within the database opened by
    31         -# database handle [db]. Return true if it is a WITHOUT ROWID table, or
    32         -# false otherwise.
    33         -#
    34         -proc is_without_rowid {tname} {
    35         -  set t [string map {' ''} $tname]
    36         -  db eval "PRAGMA index_list = '$t'" o {
    37         -    if {$o(origin) == "pk"} {
    38         -      set n $o(name)
    39         -      if {0==[db one { SELECT count(*) FROM sqlite_master WHERE name=$n }]} {
    40         -        return 1
    41         -      }
    42         -    }
    43         -  }
    44         -  return 0
    45         -}
    46         -
    47         -# Read and run TCL commands from standard input.  Used to implement
    48         -# the --tclsh option.
    49         -#
    50         -proc tclsh {} {
    51         -  set line {}
    52         -  while {![eof stdin]} {
    53         -    if {$line!=""} {
    54         -      puts -nonewline "> "
    55         -    } else {
    56         -      puts -nonewline "% "
    57         -    }
    58         -    flush stdout
    59         -    append line [gets stdin]
    60         -    if {[info complete $line]} {
    61         -      if {[catch {uplevel #0 $line} result]} {
    62         -        puts stderr "Error: $result"
    63         -      } elseif {$result!=""} {
    64         -        puts $result
    65         -      }
    66         -      set line {}
    67         -    } else {
    68         -      append line \n
    69         -    }
    70         -  }
    71         -}
    72         -
    73         -
    74         -# Get the name of the database to analyze
    75         -#
    76         -proc usage {} {
    77         -  set argv0 [file rootname [file tail [info nameofexecutable]]]
    78         -  puts stderr "Usage: $argv0 ?--pageinfo? ?--stats? database-filename"
    79         -  puts stderr {
    80         -Analyze the SQLite3 database file specified by the "database-filename"
    81         -argument and output a report detailing size and storage efficiency
    82         -information for the database and its constituent tables and indexes.
    83         -
    84         -Options:
    85         -
    86         -   --pageinfo   Show how each page of the database-file is used
    87         -
    88         -   --stats      Output SQL text that creates a new database containing
    89         -                statistics about the database that was analyzed
    90         -
    91         -   --tclsh      Run the built-in TCL interpreter interactively (for debugging)
    92         -
    93         -   --version    Show the version number of SQLite
    94         -}
    95         -  exit 1
    96         -}
    97         -set file_to_analyze {}
    98         -set flags(-pageinfo) 0
    99         -set flags(-stats) 0
   100         -set flags(-debug) 0
   101         -append argv {}
   102         -foreach arg $argv {
   103         -  if {[regexp {^-+pageinfo$} $arg]} {
   104         -    set flags(-pageinfo) 1
   105         -  } elseif {[regexp {^-+stats$} $arg]} {
   106         -    set flags(-stats) 1
   107         -  } elseif {[regexp {^-+debug$} $arg]} {
   108         -    set flags(-debug) 1
   109         -  } elseif {[regexp {^-+tclsh$} $arg]} {
   110         -    tclsh
   111         -    exit 0
   112         -  } elseif {[regexp {^-+version$} $arg]} {
   113         -    sqlite3 mem :memory:
   114         -    puts [mem one {SELECT sqlite_version()||' '||sqlite_source_id()}]
   115         -    mem close
   116         -    exit 0
   117         -  } elseif {[regexp {^-} $arg]} {
   118         -    puts stderr "Unknown option: $arg"
   119         -    usage
   120         -  } elseif {$file_to_analyze!=""} {
   121         -    usage
   122         -  } else {
   123         -    set file_to_analyze $arg
   124         -  }
   125         -}
   126         -if {$file_to_analyze==""} usage
   127         -set root_filename $file_to_analyze
   128         -regexp {^file:(//)?([^?]*)} $file_to_analyze all x1 root_filename
   129         -if {![file exists $root_filename]} {
   130         -  puts stderr "No such file: $root_filename"
   131         -  exit 1
   132         -}
   133         -if {![file readable $root_filename]} {
   134         -  puts stderr "File is not readable: $root_filename"
   135         -  exit 1
   136         -}
   137         -set true_file_size [file size $root_filename]
   138         -if {$true_file_size<512} {
   139         -  puts stderr "Empty or malformed database: $root_filename"
   140         -  exit 1
   141         -}
   142         -
   143         -# Compute the total file size assuming test_multiplexor is being used.
   144         -# Assume that SQLITE_ENABLE_8_3_NAMES might be enabled
   145         -#
   146         -set extension [file extension $root_filename]
   147         -set pattern $root_filename
   148         -append pattern {[0-3][0-9][0-9]}
   149         -foreach f [glob -nocomplain $pattern] {
   150         -  incr true_file_size [file size $f]
   151         -  set extension {}
   152         -}
   153         -if {[string length $extension]>=2 && [string length $extension]<=4} {
   154         -  set pattern [file rootname $root_filename]
   155         -  append pattern {.[0-3][0-9][0-9]}
   156         -  foreach f [glob -nocomplain $pattern] {
   157         -    incr true_file_size [file size $f]
   158         -  }
   159         -}
   160         -
   161         -# Open the database
   162         -#
   163         -if {[catch {sqlite3 db $file_to_analyze -uri 1} msg]} {
   164         -  puts stderr "error trying to open $file_to_analyze: $msg"
   165         -  exit 1
   166         -}
   167         -if {$flags(-debug)} {
   168         -  proc dbtrace {txt} {puts $txt; flush stdout;}
   169         -  db trace ::dbtrace
   170         -}
   171         -
   172         -db eval {SELECT count(*) FROM sqlite_master}
   173         -set pageSize [expr {wide([db one {PRAGMA page_size}])}]
   174         -
   175         -if {$flags(-pageinfo)} {
   176         -  db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
   177         -  db eval {SELECT name, path, pageno FROM temp.stat ORDER BY pageno} {
   178         -    puts "$pageno $name $path"
   179         -  }
   180         -  exit 0
   181         -}
   182         -if {$flags(-stats)} {
   183         -  db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
   184         -  puts "BEGIN;"
   185         -  puts "CREATE TABLE stats("
   186         -  puts "  name       STRING,           /* Name of table or index */"
   187         -  puts "  path       INTEGER,          /* Path to page from root */"
   188         -  puts "  pageno     INTEGER,          /* Page number */"
   189         -  puts "  pagetype   STRING,           /* 'internal', 'leaf' or 'overflow' */"
   190         -  puts "  ncell      INTEGER,          /* Cells on page (0 for overflow) */"
   191         -  puts "  payload    INTEGER,          /* Bytes of payload on this page */"
   192         -  puts "  unused     INTEGER,          /* Bytes of unused space on this page */"
   193         -  puts "  mx_payload INTEGER,          /* Largest payload size of all cells */"
   194         -  puts "  pgoffset   INTEGER,          /* Offset of page in file */"
   195         -  puts "  pgsize     INTEGER           /* Size of the page */"
   196         -  puts ");"
   197         -  db eval {SELECT quote(name) || ',' ||
   198         -                  quote(path) || ',' ||
   199         -                  quote(pageno) || ',' ||
   200         -                  quote(pagetype) || ',' ||
   201         -                  quote(ncell) || ',' ||
   202         -                  quote(payload) || ',' ||
   203         -                  quote(unused) || ',' ||
   204         -                  quote(mx_payload) || ',' ||
   205         -                  quote(pgoffset) || ',' ||
   206         -                  quote(pgsize) AS x FROM stat} {
   207         -    puts "INSERT INTO stats VALUES($x);"
   208         -  }
   209         -  puts "COMMIT;"
   210         -  exit 0
   211         -}
   212         -
   213         -
   214         -# In-memory database for collecting statistics. This script loops through
   215         -# the tables and indices in the database being analyzed, adding a row for each
   216         -# to an in-memory database (for which the schema is shown below). It then
   217         -# queries the in-memory db to produce the space-analysis report.
   218         -#
   219         -sqlite3 mem :memory:
   220         -if {$flags(-debug)} {
   221         -  proc dbtrace {txt} {puts $txt; flush stdout;}
   222         -  mem trace ::dbtrace
   223         -}
   224         -set tabledef {CREATE TABLE space_used(
   225         -   name clob,        -- Name of a table or index in the database file
   226         -   tblname clob,     -- Name of associated table
   227         -   is_index boolean, -- TRUE if it is an index, false for a table
   228         -   is_without_rowid boolean, -- TRUE if WITHOUT ROWID table  
   229         -   nentry int,       -- Number of entries in the BTree
   230         -   leaf_entries int, -- Number of leaf entries
   231         -   depth int,        -- Depth of the b-tree
   232         -   payload int,      -- Total amount of data stored in this table or index
   233         -   ovfl_payload int, -- Total amount of data stored on overflow pages
   234         -   ovfl_cnt int,     -- Number of entries that use overflow
   235         -   mx_payload int,   -- Maximum payload size
   236         -   int_pages int,    -- Number of interior pages used
   237         -   leaf_pages int,   -- Number of leaf pages used
   238         -   ovfl_pages int,   -- Number of overflow pages used
   239         -   int_unused int,   -- Number of unused bytes on interior pages
   240         -   leaf_unused int,  -- Number of unused bytes on primary pages
   241         -   ovfl_unused int,  -- Number of unused bytes on overflow pages
   242         -   gap_cnt int,      -- Number of gaps in the page layout
   243         -   compressed_size int  -- Total bytes stored on disk
   244         -);}
   245         -mem eval $tabledef
   246         -
   247         -# Create a temporary "dbstat" virtual table.
   248         -#
   249         -db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
   250         -db eval {CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat
   251         -         ORDER BY name, path}
   252         -db eval {DROP TABLE temp.stat}
   253         -
   254         -set isCompressed 0
   255         -set compressOverhead 0
   256         -set depth 0
   257         -set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 }
   258         -foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
   259         -
   260         -  set is_index [expr {$name!=$tblname}]
   261         -  set is_without_rowid [is_without_rowid $name]
   262         -  db eval {
   263         -    SELECT 
   264         -      sum(ncell) AS nentry,
   265         -      sum((pagetype=='leaf')*ncell) AS leaf_entries,
   266         -      sum(payload) AS payload,
   267         -      sum((pagetype=='overflow') * payload) AS ovfl_payload,
   268         -      sum(path LIKE '%+000000') AS ovfl_cnt,
   269         -      max(mx_payload) AS mx_payload,
   270         -      sum(pagetype=='internal') AS int_pages,
   271         -      sum(pagetype=='leaf') AS leaf_pages,
   272         -      sum(pagetype=='overflow') AS ovfl_pages,
   273         -      sum((pagetype=='internal') * unused) AS int_unused,
   274         -      sum((pagetype=='leaf') * unused) AS leaf_unused,
   275         -      sum((pagetype=='overflow') * unused) AS ovfl_unused,
   276         -      sum(pgsize) AS compressed_size,
   277         -      max((length(CASE WHEN path LIKE '%+%' THEN '' ELSE path END)+3)/4)
   278         -        AS depth
   279         -    FROM temp.dbstat WHERE name = $name
   280         -  } break
   281         -
   282         -  set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}]
   283         -  set storage [expr {$total_pages*$pageSize}]
   284         -  if {!$isCompressed && $storage>$compressed_size} {
   285         -    set isCompressed 1
   286         -    set compressOverhead 14
   287         -  }
   288         -
   289         -  # Column 'gap_cnt' is set to the number of non-contiguous entries in the
   290         -  # list of pages visited if the b-tree structure is traversed in a top-down
   291         -  # fashion (each node visited before its child-tree is passed). Any overflow
   292         -  # chains present are traversed from start to finish before any child-tree
   293         -  # is.
   294         -  #
   295         -  set gap_cnt 0
   296         -  set prev 0
   297         -  db eval {
   298         -    SELECT pageno, pagetype FROM temp.dbstat
   299         -     WHERE name=$name
   300         -     ORDER BY pageno
   301         -  } {
   302         -    if {$prev>0 && $pagetype=="leaf" && $pageno!=$prev+1} {
   303         -      incr gap_cnt
   304         -    }
   305         -    set prev $pageno
   306         -  }
   307         -  mem eval {
   308         -    INSERT INTO space_used VALUES(
   309         -      $name,
   310         -      $tblname,
   311         -      $is_index,
   312         -      $is_without_rowid,
   313         -      $nentry,
   314         -      $leaf_entries,
   315         -      $depth,
   316         -      $payload,     
   317         -      $ovfl_payload,
   318         -      $ovfl_cnt,   
   319         -      $mx_payload,
   320         -      $int_pages,
   321         -      $leaf_pages,  
   322         -      $ovfl_pages, 
   323         -      $int_unused, 
   324         -      $leaf_unused,
   325         -      $ovfl_unused,
   326         -      $gap_cnt,
   327         -      $compressed_size
   328         -    );
   329         -  }
   330         -}
   331         -
   332         -proc integerify {real} {
   333         -  if {[string is double -strict $real]} {
   334         -    return [expr {wide($real)}]
   335         -  } else {
   336         -    return 0
   337         -  }
   338         -}
   339         -mem function int integerify
   340         -
   341         -# Quote a string for use in an SQL query. Examples:
   342         -#
   343         -# [quote {hello world}]   == {'hello world'}
   344         -# [quote {hello world's}] == {'hello world''s'}
   345         -#
   346         -proc quote {txt} {
   347         -  return [string map {' ''} $txt]
   348         -}
   349         -
   350         -# Output a title line
   351         -#
   352         -proc titleline {title} {
   353         -  if {$title==""} {
   354         -    puts [string repeat * 79]
   355         -  } else {
   356         -    set len [string length $title]
   357         -    set stars [string repeat * [expr 79-$len-5]]
   358         -    puts "*** $title $stars"
   359         -  }
   360         -}
   361         -
   362         -# Generate a single line of output in the statistics section of the
   363         -# report.
   364         -#
   365         -proc statline {title value {extra {}}} {
   366         -  set len [string length $title]
   367         -  set dots [string repeat . [expr 50-$len]]
   368         -  set len [string length $value]
   369         -  set sp2 [string range {          } $len end]
   370         -  if {$extra ne ""} {
   371         -    set extra " $extra"
   372         -  }
   373         -  puts "$title$dots $value$sp2$extra"
   374         -}
   375         -
   376         -# Generate a formatted percentage value for $num/$denom
   377         -#
   378         -proc percent {num denom {of {}}} {
   379         -  if {$denom==0.0} {return ""}
   380         -  set v [expr {$num*100.0/$denom}]
   381         -  set of {}
   382         -  if {$v==100.0 || $v<0.001 || ($v>1.0 && $v<99.0)} {
   383         -    return [format {%5.1f%% %s} $v $of]
   384         -  } elseif {$v<0.1 || $v>99.9} {
   385         -    return [format {%7.3f%% %s} $v $of]
   386         -  } else {
   387         -    return [format {%6.2f%% %s} $v $of]
   388         -  }
   389         -}
   390         -
   391         -proc divide {num denom} {
   392         -  if {$denom==0} {return 0.0}
   393         -  return [format %.2f [expr double($num)/double($denom)]]
   394         -}
   395         -
   396         -# Generate a subreport that covers some subset of the database.
   397         -# the $where clause determines which subset to analyze.
   398         -#
   399         -proc subreport {title where showFrag} {
   400         -  global pageSize file_pgcnt compressOverhead
   401         -
   402         -  # Query the in-memory database for the sum of various statistics 
   403         -  # for the subset of tables/indices identified by the WHERE clause in
   404         -  # $where. Note that even if the WHERE clause matches no rows, the
   405         -  # following query returns exactly one row (because it is an aggregate).
   406         -  #
   407         -  # The results of the query are stored directly by SQLite into local 
   408         -  # variables (i.e. $nentry, $payload etc.).
   409         -  #
   410         -  mem eval "
   411         -    SELECT
   412         -      int(sum(
   413         -        CASE WHEN (is_without_rowid OR is_index) THEN nentry 
   414         -             ELSE leaf_entries 
   415         -        END
   416         -      )) AS nentry,
   417         -      int(sum(payload)) AS payload,
   418         -      int(sum(ovfl_payload)) AS ovfl_payload,
   419         -      max(mx_payload) AS mx_payload,
   420         -      int(sum(ovfl_cnt)) as ovfl_cnt,
   421         -      int(sum(leaf_pages)) AS leaf_pages,
   422         -      int(sum(int_pages)) AS int_pages,
   423         -      int(sum(ovfl_pages)) AS ovfl_pages,
   424         -      int(sum(leaf_unused)) AS leaf_unused,
   425         -      int(sum(int_unused)) AS int_unused,
   426         -      int(sum(ovfl_unused)) AS ovfl_unused,
   427         -      int(sum(gap_cnt)) AS gap_cnt,
   428         -      int(sum(compressed_size)) AS compressed_size,
   429         -      int(max(depth)) AS depth,
   430         -      count(*) AS cnt
   431         -    FROM space_used WHERE $where" {} {}
   432         -
   433         -  # Output the sub-report title, nicely decorated with * characters.
   434         -  #
   435         -  puts ""
   436         -  titleline $title
   437         -  puts ""
   438         -
   439         -  # Calculate statistics and store the results in TCL variables, as follows:
   440         -  #
   441         -  # total_pages: Database pages consumed.
   442         -  # total_pages_percent: Pages consumed as a percentage of the file.
   443         -  # storage: Bytes consumed.
   444         -  # payload_percent: Payload bytes used as a percentage of $storage.
   445         -  # total_unused: Unused bytes on pages.
   446         -  # avg_payload: Average payload per btree entry.
   447         -  # avg_fanout: Average fanout for internal pages.
   448         -  # avg_unused: Average unused bytes per btree entry.
   449         -  # avg_meta: Average metadata overhead per entry.
   450         -  # ovfl_cnt_percent: Percentage of btree entries that use overflow pages.
   451         -  #
   452         -  set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}]
   453         -  set total_pages_percent [percent $total_pages $file_pgcnt]
   454         -  set storage [expr {$total_pages*$pageSize}]
   455         -  set payload_percent [percent $payload $storage {of storage consumed}]
   456         -  set total_unused [expr {$ovfl_unused+$int_unused+$leaf_unused}]
   457         -  set avg_payload [divide $payload $nentry]
   458         -  set avg_unused [divide $total_unused $nentry]
   459         -  set total_meta [expr {$storage - $payload - $total_unused}]
   460         -  set total_meta [expr {$total_meta + 4*($ovfl_pages - $ovfl_cnt)}]
   461         -  set meta_percent [percent $total_meta $storage {of metadata}]
   462         -  set avg_meta [divide $total_meta $nentry]
   463         -  if {$int_pages>0} {
   464         -    # TODO: Is this formula correct?
   465         -    set nTab [mem eval "
   466         -      SELECT count(*) FROM (
   467         -          SELECT DISTINCT tblname FROM space_used WHERE $where AND is_index=0
   468         -      )
   469         -    "]
   470         -    set avg_fanout [mem eval "
   471         -      SELECT (sum(leaf_pages+int_pages)-$nTab)/sum(int_pages) FROM space_used
   472         -          WHERE $where
   473         -    "]
   474         -    set avg_fanout [format %.2f $avg_fanout]
   475         -  }
   476         -  set ovfl_cnt_percent [percent $ovfl_cnt $nentry {of all entries}]
   477         -
   478         -  # Print out the sub-report statistics.
   479         -  #
   480         -  statline {Percentage of total database} $total_pages_percent
   481         -  statline {Number of entries} $nentry
   482         -  statline {Bytes of storage consumed} $storage
   483         -  if {$compressed_size!=$storage} {
   484         -    set compressed_size [expr {$compressed_size+$compressOverhead*$total_pages}]
   485         -    set pct [expr {$compressed_size*100.0/$storage}]
   486         -    set pct [format {%5.1f%%} $pct]
   487         -    statline {Bytes used after compression} $compressed_size $pct
   488         -  }
   489         -  statline {Bytes of payload} $payload $payload_percent
   490         -  statline {Bytes of metadata} $total_meta $meta_percent
   491         -  if {$cnt==1} {statline {B-tree depth} $depth}
   492         -  statline {Average payload per entry} $avg_payload
   493         -  statline {Average unused bytes per entry} $avg_unused
   494         -  statline {Average metadata per entry} $avg_meta
   495         -  if {[info exists avg_fanout]} {
   496         -    statline {Average fanout} $avg_fanout
   497         -  }
   498         -  if {$showFrag && $total_pages>1} {
   499         -    set fragmentation [percent $gap_cnt [expr {$total_pages-1}]]
   500         -    statline {Non-sequential pages} $gap_cnt $fragmentation
   501         -  }
   502         -  statline {Maximum payload per entry} $mx_payload
   503         -  statline {Entries that use overflow} $ovfl_cnt $ovfl_cnt_percent
   504         -  if {$int_pages>0} {
   505         -    statline {Index pages used} $int_pages
   506         -  }
   507         -  statline {Primary pages used} $leaf_pages
   508         -  statline {Overflow pages used} $ovfl_pages
   509         -  statline {Total pages used} $total_pages
   510         -  if {$int_unused>0} {
   511         -    set int_unused_percent [
   512         -         percent $int_unused [expr {$int_pages*$pageSize}] {of index space}]
   513         -    statline "Unused bytes on index pages" $int_unused $int_unused_percent
   514         -  }
   515         -  statline "Unused bytes on primary pages" $leaf_unused [
   516         -     percent $leaf_unused [expr {$leaf_pages*$pageSize}] {of primary space}]
   517         -  statline "Unused bytes on overflow pages" $ovfl_unused [
   518         -     percent $ovfl_unused [expr {$ovfl_pages*$pageSize}] {of overflow space}]
   519         -  statline "Unused bytes on all pages" $total_unused [
   520         -               percent $total_unused $storage {of all space}]
   521         -  return 1
   522         -}
   523         -
   524         -# Calculate the overhead in pages caused by auto-vacuum. 
   525         -#
   526         -# This procedure calculates and returns the number of pages used by the 
   527         -# auto-vacuum 'pointer-map'. If the database does not support auto-vacuum,
   528         -# then 0 is returned. The two arguments are the size of the database file in
   529         -# pages and the page size used by the database (in bytes).
   530         -proc autovacuum_overhead {filePages pageSize} {
   531         -
   532         -  # Set $autovacuum to non-zero for databases that support auto-vacuum.
   533         -  set autovacuum [db one {PRAGMA auto_vacuum}]
   534         -
   535         -  # If the database is not an auto-vacuum database or the file consists
   536         -  # of one page only then there is no overhead for auto-vacuum. Return zero.
   537         -  if {0==$autovacuum || $filePages==1} {
   538         -    return 0
   539         -  }
   540         -
   541         -  # The number of entries on each pointer map page. The layout of the
   542         -  # database file is one pointer-map page, followed by $ptrsPerPage other
   543         -  # pages, followed by a pointer-map page etc. The first pointer-map page
   544         -  # is the second page of the file overall.
   545         -  set ptrsPerPage [expr double($pageSize/5)]
   546         -
   547         -  # Return the number of pointer map pages in the database.
   548         -  return [expr wide(ceil( ($filePages-1.0)/($ptrsPerPage+1.0) ))]
   549         -}
   550         -
   551         -
   552         -# Calculate the summary statistics for the database and store the results
   553         -# in TCL variables. They are output below. Variables are as follows:
   554         -#
   555         -# pageSize:      Size of each page in bytes.
   556         -# file_bytes:    File size in bytes.
   557         -# file_pgcnt:    Number of pages in the file.
   558         -# file_pgcnt2:   Number of pages in the file (calculated).
   559         -# av_pgcnt:      Pages consumed by the auto-vacuum pointer-map.
   560         -# av_percent:    Percentage of the file consumed by auto-vacuum pointer-map.
   561         -# inuse_pgcnt:   Data pages in the file.
   562         -# inuse_percent: Percentage of pages used to store data.
   563         -# free_pgcnt:    Free pages calculated as (<total pages> - <in-use pages>)
   564         -# free_pgcnt2:   Free pages in the file according to the file header.
   565         -# free_percent:  Percentage of file consumed by free pages (calculated).
   566         -# free_percent2: Percentage of file consumed by free pages (header).
   567         -# ntable:        Number of tables in the db.
   568         -# nindex:        Number of indices in the db.
   569         -# nautoindex:    Number of indices created automatically.
   570         -# nmanindex:     Number of indices created manually.
   571         -# user_payload:  Number of bytes of payload in table btrees 
   572         -#                (not including sqlite_master)
   573         -# user_percent:  $user_payload as a percentage of total file size.
   574         -
   575         -### The following, setting $file_bytes based on the actual size of the file
   576         -### on disk, causes this tool to choke on zipvfs databases. So set it based
   577         -### on the return of [PRAGMA page_count] instead.
   578         -if 0 {
   579         -  set file_bytes  [file size $file_to_analyze]
   580         -  set file_pgcnt  [expr {$file_bytes/$pageSize}]
   581         -}
   582         -set file_pgcnt  [db one {PRAGMA page_count}]
   583         -set file_bytes  [expr {$file_pgcnt * $pageSize}]
   584         -
   585         -set av_pgcnt    [autovacuum_overhead $file_pgcnt $pageSize]
   586         -set av_percent  [percent $av_pgcnt $file_pgcnt]
   587         -
   588         -set sql {SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM space_used}
   589         -set inuse_pgcnt   [expr wide([mem eval $sql])]
   590         -set inuse_percent [percent $inuse_pgcnt $file_pgcnt]
   591         -
   592         -set free_pgcnt    [expr {$file_pgcnt-$inuse_pgcnt-$av_pgcnt}]
   593         -set free_percent  [percent $free_pgcnt $file_pgcnt]
   594         -set free_pgcnt2   [db one {PRAGMA freelist_count}]
   595         -set free_percent2 [percent $free_pgcnt2 $file_pgcnt]
   596         -
   597         -set file_pgcnt2 [expr {$inuse_pgcnt+$free_pgcnt2+$av_pgcnt}]
   598         -
   599         -set ntable [db eval {SELECT count(*)+1 FROM sqlite_master WHERE type='table'}]
   600         -set nindex [db eval {SELECT count(*) FROM sqlite_master WHERE type='index'}]
   601         -set sql {SELECT count(*) FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'}
   602         -set nautoindex [db eval $sql]
   603         -set nmanindex [expr {$nindex-$nautoindex}]
   604         -
   605         -# set total_payload [mem eval "SELECT sum(payload) FROM space_used"]
   606         -set user_payload [mem one {SELECT int(sum(payload)) FROM space_used
   607         -     WHERE NOT is_index AND name NOT LIKE 'sqlite_master'}]
   608         -set user_percent [percent $user_payload $file_bytes]
   609         -
   610         -# Output the summary statistics calculated above.
   611         -#
   612         -puts "/** Disk-Space Utilization Report For $root_filename"
   613         -puts ""
   614         -statline {Page size in bytes} $pageSize
   615         -statline {Pages in the whole file (measured)} $file_pgcnt
   616         -statline {Pages in the whole file (calculated)} $file_pgcnt2
   617         -statline {Pages that store data} $inuse_pgcnt $inuse_percent
   618         -statline {Pages on the freelist (per header)} $free_pgcnt2 $free_percent2
   619         -statline {Pages on the freelist (calculated)} $free_pgcnt $free_percent
   620         -statline {Pages of auto-vacuum overhead} $av_pgcnt $av_percent
   621         -statline {Number of tables in the database} $ntable
   622         -statline {Number of indices} $nindex
   623         -statline {Number of defined indices} $nmanindex
   624         -statline {Number of implied indices} $nautoindex
   625         -if {$isCompressed} {
   626         -  statline {Size of uncompressed content in bytes} $file_bytes
   627         -  set efficiency [percent $true_file_size $file_bytes]
   628         -  statline {Size of compressed file on disk} $true_file_size $efficiency
   629         -} else {
   630         -  statline {Size of the file in bytes} $file_bytes
   631         -}
   632         -statline {Bytes of user payload stored} $user_payload $user_percent
   633         -
   634         -# Output table rankings
   635         -#
   636         -puts ""
   637         -titleline "Page counts for all tables with their indices"
   638         -puts ""
   639         -mem eval {SELECT tblname, count(*) AS cnt, 
   640         -              int(sum(int_pages+leaf_pages+ovfl_pages)) AS size
   641         -          FROM space_used GROUP BY tblname ORDER BY size+0 DESC, tblname} {} {
   642         -  statline [string toupper $tblname] $size [percent $size $file_pgcnt]
   643         -}
   644         -puts ""
   645         -titleline "Page counts for all tables and indices separately"
   646         -puts ""
   647         -mem eval {
   648         -  SELECT
   649         -       upper(name) AS nm,
   650         -       int(int_pages+leaf_pages+ovfl_pages) AS size
   651         -    FROM space_used
   652         -   ORDER BY size+0 DESC, name} {} {
   653         -  statline $nm $size [percent $size $file_pgcnt]
   654         -}
   655         -if {$isCompressed} {
   656         -  puts ""
   657         -  titleline "Bytes of disk space used after compression"
   658         -  puts ""
   659         -  set csum 0
   660         -  mem eval {SELECT tblname,
   661         -                  int(sum(compressed_size)) +
   662         -                         $compressOverhead*sum(int_pages+leaf_pages+ovfl_pages)
   663         -                        AS csize
   664         -          FROM space_used GROUP BY tblname ORDER BY csize+0 DESC, tblname} {} {
   665         -    incr csum $csize
   666         -    statline [string toupper $tblname] $csize [percent $csize $true_file_size]
   667         -  }
   668         -  set overhead [expr {$true_file_size - $csum}]
   669         -  if {$overhead>0} {
   670         -    statline {Header and free space} $overhead [percent $overhead $true_file_size]
   671         -  }
   672         -}
   673         -
   674         -# Output subreports
   675         -#
   676         -if {$nindex>0} {
   677         -  subreport {All tables and indices} 1 0
   678         -}
   679         -subreport {All tables} {NOT is_index} 0
   680         -if {$nindex>0} {
   681         -  subreport {All indices} {is_index} 0
   682         -}
   683         -foreach tbl [mem eval {SELECT DISTINCT tblname name FROM space_used
   684         -                       ORDER BY name}] {
   685         -  set qn [quote $tbl]
   686         -  set name [string toupper $tbl]
   687         -  set n [mem eval {SELECT count(*) FROM space_used WHERE tblname=$tbl}]
   688         -  if {$n>1} {
   689         -    set idxlist [mem eval "SELECT name FROM space_used
   690         -                            WHERE tblname='$qn' AND is_index
   691         -                            ORDER BY 1"]
   692         -    subreport "Table $name and all its indices" "tblname='$qn'" 0
   693         -    subreport "Table $name w/o any indices" "name='$qn'" 1
   694         -    if {[llength $idxlist]>1} {
   695         -      subreport "Indices of table $name" "tblname='$qn' AND is_index" 0
   696         -    }
   697         -    foreach idx $idxlist {
   698         -      set qidx [quote $idx]
   699         -      subreport "Index [string toupper $idx] of table $name" "name='$qidx'" 1
   700         -    }
   701         -  } else {
   702         -    subreport "Table $name" "name='$qn'" 1
   703         -  }
   704         -}
   705         -
   706         -# Output instructions on what the numbers above mean.
   707         -#
   708         -puts ""
   709         -titleline Definitions
   710         -puts {
   711         -Page size in bytes
   712         -
   713         -    The number of bytes in a single page of the database file.  
   714         -    Usually 1024.
   715         -
   716         -Number of pages in the whole file
   717         -}
   718         -puts "    The number of $pageSize-byte pages that go into forming the complete
   719         -    database"
   720         -puts {
   721         -Pages that store data
   722         -
   723         -    The number of pages that store data, either as primary B*Tree pages or
   724         -    as overflow pages.  The number at the right is the data pages divided by
   725         -    the total number of pages in the file.
   726         -
   727         -Pages on the freelist
   728         -
   729         -    The number of pages that are not currently in use but are reserved for
   730         -    future use.  The percentage at the right is the number of freelist pages
   731         -    divided by the total number of pages in the file.
   732         -
   733         -Pages of auto-vacuum overhead
   734         -
   735         -    The number of pages that store data used by the database to facilitate
   736         -    auto-vacuum. This is zero for databases that do not support auto-vacuum.
   737         -
   738         -Number of tables in the database
   739         -
   740         -    The number of tables in the database, including the SQLITE_MASTER table
   741         -    used to store schema information.
   742         -
   743         -Number of indices
   744         -
   745         -    The total number of indices in the database.
   746         -
   747         -Number of defined indices
   748         -
   749         -    The number of indices created using an explicit CREATE INDEX statement.
   750         -
   751         -Number of implied indices
   752         -
   753         -    The number of indices used to implement PRIMARY KEY or UNIQUE constraints
   754         -    on tables.
   755         -
   756         -Size of the file in bytes
   757         -
   758         -    The total amount of disk space used by the entire database files.
   759         -
   760         -Bytes of user payload stored
   761         -
   762         -    The total number of bytes of user payload stored in the database. The
   763         -    schema information in the SQLITE_MASTER table is not counted when
   764         -    computing this number.  The percentage at the right shows the payload
   765         -    divided by the total file size.
   766         -
   767         -Percentage of total database
   768         -
   769         -    The amount of the complete database file that is devoted to storing
   770         -    information described by this category.
   771         -
   772         -Number of entries
   773         -
   774         -    The total number of B-Tree key/value pairs stored under this category.
   775         -
   776         -Bytes of storage consumed
   777         -
   778         -    The total amount of disk space required to store all B-Tree entries
   779         -    under this category.  The is the total number of pages used times
   780         -    the pages size.
   781         -
   782         -Bytes of payload
   783         -
   784         -    The amount of payload stored under this category.  Payload is the data
   785         -    part of table entries and the key part of index entries.  The percentage
   786         -    at the right is the bytes of payload divided by the bytes of storage 
   787         -    consumed.
   788         -
   789         -Bytes of metadata
   790         -
   791         -    The amount of formatting and structural information stored in the
   792         -    table or index.  Metadata includes the btree page header, the cell pointer
   793         -    array, the size field for each cell, the left child pointer or non-leaf
   794         -    cells, the overflow pointers for overflow cells, and the rowid value for
   795         -    rowid table cells.  In other words, metadata is everything that is neither
   796         -    unused space nor content.  The record header in the payload is counted as
   797         -    content, not metadata.
   798         -
   799         -Average payload per entry
   800         -
   801         -    The average amount of payload on each entry.  This is just the bytes of
   802         -    payload divided by the number of entries.
   803         -
   804         -Average unused bytes per entry
   805         -
   806         -    The average amount of free space remaining on all pages under this
   807         -    category on a per-entry basis.  This is the number of unused bytes on
   808         -    all pages divided by the number of entries.
   809         -
   810         -Non-sequential pages
   811         -
   812         -    The number of pages in the table or index that are out of sequence.
   813         -    Many filesystems are optimized for sequential file access so a small
   814         -    number of non-sequential pages might result in faster queries,
   815         -    especially for larger database files that do not fit in the disk cache.
   816         -    Note that after running VACUUM, the root page of each table or index is
   817         -    at the beginning of the database file and all other pages are in a
   818         -    separate part of the database file, resulting in a single non-
   819         -    sequential page.
   820         -
   821         -Maximum payload per entry
   822         -
   823         -    The largest payload size of any entry.
   824         -
   825         -Entries that use overflow
   826         -
   827         -    The number of entries that user one or more overflow pages.
   828         -
   829         -Total pages used
   830         -
   831         -    This is the number of pages used to hold all information in the current
   832         -    category.  This is the sum of index, primary, and overflow pages.
   833         -
   834         -Index pages used
   835         -
   836         -    This is the number of pages in a table B-tree that hold only key (rowid)
   837         -    information and no data.
   838         -
   839         -Primary pages used
   840         -
   841         -    This is the number of B-tree pages that hold both key and data.
   842         -
   843         -Overflow pages used
   844         -
   845         -    The total number of overflow pages used for this category.
   846         -
   847         -Unused bytes on index pages
   848         -
   849         -    The total number of bytes of unused space on all index pages.  The
   850         -    percentage at the right is the number of unused bytes divided by the
   851         -    total number of bytes on index pages.
   852         -
   853         -Unused bytes on primary pages
   854         -
   855         -    The total number of bytes of unused space on all primary pages.  The
   856         -    percentage at the right is the number of unused bytes divided by the
   857         -    total number of bytes on primary pages.
   858         -
   859         -Unused bytes on overflow pages
   860         -
   861         -    The total number of bytes of unused space on all overflow pages.  The
   862         -    percentage at the right is the number of unused bytes divided by the
   863         -    total number of bytes on overflow pages.
   864         -
   865         -Unused bytes on all pages
   866         -
   867         -    The total number of bytes of unused space on all primary and overflow 
   868         -    pages.  The percentage at the right is the number of unused bytes 
   869         -    divided by the total number of bytes.
   870         -}
   871         -
   872         -# Output a dump of the in-memory database. This can be used for more
   873         -# complex offline analysis.
   874         -#
   875         -titleline {}
   876         -puts "The entire text of this report can be sourced into any SQL database"
   877         -puts "engine for further analysis.  All of the text above is an SQL comment."
   878         -puts "The data used to generate this report follows:"
   879         -puts "*/"
   880         -puts "BEGIN;"
   881         -puts $tabledef
   882         -unset -nocomplain x
   883         -mem eval {SELECT * FROM space_used} x {
   884         -  puts -nonewline "INSERT INTO space_used VALUES"
   885         -  set sep (
   886         -  foreach col $x(*) {
   887         -    set v $x($col)
   888         -    if {$v=="" || ![string is double $v]} {set v '[quote $v]'}
   889         -    puts -nonewline $sep$v
   890         -    set sep ,
   891         -  }
   892         -  puts ");"
   893         -}
   894         -puts "COMMIT;"
   895         -
   896         -} err]} {
   897         -  puts "ERROR: $err"
   898         -  puts $errorInfo
   899         -  exit 1
   900         -}
           24  +INCLUDE $ROOT/tool/spaceanal.tcl
   901     25   END_STRING
   902     26   ;
   903     27   }