summaryrefslogtreecommitdiff
path: root/tool
diff options
context:
space:
mode:
authorHans-Christoph Steiner <hans@eds.org>2014-10-16 22:51:35 -0400
committerHans-Christoph Steiner <hans@eds.org>2014-10-16 22:51:35 -0400
commit569c6676a6ddb0ff73821d7693b5e18ddef809b9 (patch)
tree833538da7bba39105daff45e265aef386a200acd /tool
parent08119c361d1181b3e8f1abb429236e488a664753 (diff)
Imported Upstream version 3.2.0upstream
Diffstat (limited to 'tool')
-rwxr-xr-xtool/build-all-msvc.bat80
-rw-r--r--tool/fast_vacuum.c234
-rw-r--r--tool/lemon.c297
-rw-r--r--tool/logest.c170
-rw-r--r--tool/mkautoconfamal.sh83
-rw-r--r--tool/mkkeywordhash.c10
-rw-r--r--tool/mkpragmatab.tcl434
-rw-r--r--tool/mksqlite3c-noext.tcl2
-rw-r--r--tool/mksqlite3c.tcl17
-rw-r--r--tool/mksqlite3internalh.tcl2
-rw-r--r--tool/mkvsix.tcl234
-rw-r--r--tool/omittest.tcl1
-rw-r--r--tool/pagesig.c92
-rw-r--r--tool/showdb.c279
-rw-r--r--tool/showjournal.c19
-rw-r--r--tool/showstat4.c157
-rw-r--r--tool/showwal.c48
-rw-r--r--tool/spaceanal.tcl125
-rw-r--r--tool/vdbe-compress.tcl12
-rw-r--r--tool/vdbe_profile.tcl82
-rw-r--r--tool/warnings.sh6
-rw-r--r--tool/win/sqlite.vsixbin32816 -> 32825 bytes
22 files changed, 2133 insertions, 251 deletions
diff --git a/tool/build-all-msvc.bat b/tool/build-all-msvc.bat
index 758036f..1fb61d4 100755
--- a/tool/build-all-msvc.bat
+++ b/tool/build-all-msvc.bat
@@ -147,6 +147,17 @@ IF NOT DEFINED CONFIGURATIONS (
%_VECHO% Configurations = '%CONFIGURATIONS%'
REM
+REM NOTE: If the command used to invoke NMAKE is not already set, use the
+REM default.
+REM
+IF NOT DEFINED NMAKE_CMD (
+ SET NMAKE_CMD=nmake -B -f Makefile.msc
+)
+
+%_VECHO% NmakeCmd = '%NMAKE_CMD%'
+%_VECHO% NmakeArgs = '%NMAKE_ARGS%'
+
+REM
REM NOTE: Setup environment variables to translate between the MSVC platform
REM names and the names to be used for the platform-specific binary
REM directories.
@@ -203,8 +214,8 @@ SET TOOLPATH=%gawk.exe_PATH%;%tclsh85.exe_PATH%
%_VECHO% ToolPath = '%TOOLPATH%'
REM
-REM NOTE: Check for MSVC 2012 because the Windows SDK directory handling is
-REM slightly different for that version.
+REM NOTE: Check for MSVC 2012/2013 because the Windows SDK directory handling
+REM is slightly different for those versions.
REM
IF "%VisualStudioVersion%" == "11.0" (
REM
@@ -214,6 +225,14 @@ IF "%VisualStudioVersion%" == "11.0" (
IF NOT DEFINED NSDKLIBPATH (
SET SET_NSDKLIBPATH=1
)
+) ELSE IF "%VisualStudioVersion%" == "12.0" (
+ REM
+ REM NOTE: If the Windows SDK library path has already been set, do not set
+ REM it to something else later on.
+ REM
+ IF NOT DEFINED NSDKLIBPATH (
+ SET SET_NSDKLIBPATH=1
+ )
) ELSE (
CALL :fn_UnsetVariable SET_NSDKLIBPATH
)
@@ -230,6 +249,7 @@ GOTO set_vcvarsall_done
:set_vcvarsall_phone
SET VCVARSALL=%VCINSTALLDIR%\WPSDK\WP80\vcvarsphoneall.bat
:set_vcvarsall_done
+SET VCVARSALL=%VCVARSALL:\\=\%
REM
REM NOTE: This is the outer loop. There should be exactly one iteration per
@@ -244,7 +264,7 @@ FOR %%P IN (%PLATFORMS%) DO (
CALL :fn_CopyVariable %%P_NAME PLATFORMNAME
REM
- REM NOTE: This is the inner loop. There should be exactly one iteration.
+ REM NOTE: This is the second loop. There should be exactly one iteration.
REM This loop is necessary because the PlatformName environment
REM variable was set above and that value is needed by some of the
REM commands contained in the inner loop. If these commands were
@@ -257,9 +277,11 @@ FOR %%P IN (%PLATFORMS%) DO (
REM and/or Visual Studio. This block may need to be updated in the
REM future to account for additional environment variables.
REM
+ CALL :fn_UnsetVariable CommandPromptType
CALL :fn_UnsetVariable DevEnvDir
CALL :fn_UnsetVariable ExtensionSdkDir
CALL :fn_UnsetVariable Framework35Version
+ CALL :fn_UnsetVariable Framework40Version
CALL :fn_UnsetVariable FrameworkDir
CALL :fn_UnsetVariable FrameworkDir32
CALL :fn_UnsetVariable FrameworkVersion
@@ -275,18 +297,26 @@ FOR %%P IN (%PLATFORMS%) DO (
CALL :fn_UnsetVariable WindowsSdkDir
CALL :fn_UnsetVariable WindowsSdkDir_35
CALL :fn_UnsetVariable WindowsSdkDir_old
+ CALL :fn_UnsetVariable WindowsSDK_ExecutablePath_x86
+ CALL :fn_UnsetVariable WindowsSDK_ExecutablePath_x64
REM
REM NOTE: Reset the PATH here to the absolute bare minimum required.
REM
SET PATH=%TOOLPATH%;%SystemRoot%\System32;%SystemRoot%
+ REM
+ REM NOTE: This is the inner loop. There are normally two iterations, one
+ REM for each supported build configuration, e.g. Debug or Retail.
+ REM
FOR %%B IN (%CONFIGURATIONS%) DO (
REM
REM NOTE: When preparing the debug build, set the DEBUG and MEMDEBUG
REM environment variables to be picked up by the MSVC makefile
REM itself.
REM
+ %_AECHO% Building the %%B configuration for platform %%P with name %%D...
+
IF /I "%%B" == "Debug" (
SET DEBUG=2
SET MEMDEBUG=1
@@ -310,6 +340,10 @@ FOR %%P IN (%PLATFORMS%) DO (
REM platform to the platform-specific directory beneath the
REM binary directory.
REM
+ REM 5. Unless prevented from doing so, copy the "sqlite3.pdb"
+ REM symbols file for this platform to the platform-specific
+ REM directory beneath the binary directory.
+ REM
"%ComSpec%" /C (
REM
REM NOTE: Attempt to setup the MSVC environment for this platform.
@@ -337,21 +371,36 @@ FOR %%P IN (%PLATFORMS%) DO (
)
REM
- REM NOTE: When using MSVC 2012, the native SDK path cannot simply use
- REM the "lib" sub-directory beneath the location specified in the
- REM WindowsSdkDir environment variable because that location does
- REM not actually contain the necessary library files for x86.
- REM This must be done for each iteration because it relies upon
- REM the WindowsSdkDir environment variable being set by the batch
- REM file used to setup the MSVC environment.
+ REM NOTE: When using MSVC 2012 and/or 2013, the native SDK path cannot
+ REM simply use the "lib" sub-directory beneath the location
+ REM specified in the WindowsSdkDir environment variable because
+ REM that location does not actually contain the necessary library
+ REM files for x86. This must be done for each iteration because
+ REM it relies upon the WindowsSdkDir environment variable being
+ REM set by the batch file used to setup the MSVC environment.
REM
IF DEFINED SET_NSDKLIBPATH (
+ REM
+ REM NOTE: The Windows Phone SDK has a slightly different directory
+ REM structure and must be handled specially here.
+ REM
IF DEFINED WindowsPhoneKitDir (
CALL :fn_CopyVariable WindowsPhoneKitDir NSDKLIBPATH
CALL :fn_AppendVariable NSDKLIBPATH \lib\x86
) ELSE IF DEFINED WindowsSdkDir (
CALL :fn_CopyVariable WindowsSdkDir NSDKLIBPATH
- CALL :fn_AppendVariable NSDKLIBPATH \lib\win8\um\x86
+
+ REM
+ REM NOTE: The Windows 8.1 SDK has a slightly different directory
+ REM naming convention.
+ REM
+ IF DEFINED USE_WINV63_NSDKLIBPATH (
+ CALL :fn_AppendVariable NSDKLIBPATH \lib\winv6.3\um\x86
+ ) ELSE IF "%VisualStudioVersion%" == "12.0" (
+ CALL :fn_AppendVariable NSDKLIBPATH \..\8.0\lib\win8\um\x86
+ ) ELSE (
+ CALL :fn_AppendVariable NSDKLIBPATH \lib\win8\um\x86
+ )
)
)
@@ -362,7 +411,7 @@ FOR %%P IN (%PLATFORMS%) DO (
REM file, etc.
REM
IF NOT DEFINED NOCLEAN (
- %__ECHO% nmake -f Makefile.msc clean
+ %__ECHO% %NMAKE_CMD% clean
IF ERRORLEVEL 1 (
ECHO Failed to clean for platform %%P.
@@ -374,7 +423,8 @@ FOR %%P IN (%PLATFORMS%) DO (
REM need to remove the build output for the files we are
REM specifically wanting to build for each platform.
REM
- %__ECHO% DEL /Q sqlite3.dll sqlite3.lib sqlite3.pdb
+ %_AECHO% Cleaning final output files only...
+ %__ECHO% DEL /Q *.lo sqlite3.dll sqlite3.lib sqlite3.pdb
)
REM
@@ -384,7 +434,7 @@ FOR %%P IN (%PLATFORMS%) DO (
REM Also, disable looking for and/or linking to the native Tcl
REM runtime library.
REM
- %__ECHO% nmake -f Makefile.msc sqlite3.dll XCOMPILE=1 USE_NATIVE_LIBPATHS=1 NO_TCL=1 %NMAKE_ARGS%
+ %__ECHO% %NMAKE_CMD% sqlite3.dll XCOMPILE=1 USE_NATIVE_LIBPATHS=1 NO_TCL=1 %NMAKE_ARGS%
IF ERRORLEVEL 1 (
ECHO Failed to build %%B "sqlite3.dll" for platform %%P.
@@ -462,9 +512,9 @@ GOTO no_errors
GOTO :EOF
:fn_CopyVariable
- SETLOCAL
IF NOT DEFINED %1 GOTO :EOF
IF "%2" == "" GOTO :EOF
+ SETLOCAL
SET __ECHO_CMD=ECHO %%%1%%
FOR /F "delims=" %%V IN ('%__ECHO_CMD%') DO (
SET VALUE=%%V
diff --git a/tool/fast_vacuum.c b/tool/fast_vacuum.c
new file mode 100644
index 0000000..6a50dcc
--- /dev/null
+++ b/tool/fast_vacuum.c
@@ -0,0 +1,234 @@
+/*
+** 2013-10-01
+**
+** 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 program implements a high-speed version of the VACUUM command.
+** It repacks an SQLite database to remove as much unused space as
+** possible and to relocate content sequentially in the file.
+**
+** This program runs faster and uses less temporary disk space than the
+** built-in VACUUM command. On the other hand, this program has a number
+** of important restrictions relative to the built-in VACUUM command.
+**
+** (1) The caller must ensure that no other processes are accessing the
+** database file while the vacuum is taking place. The usual SQLite
+** file locking is insufficient for this. The caller must use
+** external means to make sure only this one routine is reading and
+** writing the database.
+**
+** (2) Database reconfiguration such as page size or auto_vacuum changes
+** are not supported by this utility.
+**
+** (3) The database file might be renamed if a power loss or crash
+** occurs at just the wrong moment. Recovery must be prepared to
+** to deal with the possibly changed filename.
+**
+** This program is intended as a *Demonstration Only*. The intent of this
+** program is to provide example code that application developers can use
+** when creating similar functionality in their applications.
+**
+** To compile this program:
+**
+** cc fast_vacuum.c sqlite3.c
+**
+** Add whatever linker options are required. (Example: "-ldl -lpthread").
+** Then to run the program:
+**
+** ./a.out file-to-vacuum
+**
+*/
+#include "sqlite3.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+** Finalize a prepared statement. If an error has occurred, print the
+** error message and exit.
+*/
+static void vacuumFinalize(sqlite3_stmt *pStmt){
+ sqlite3 *db = sqlite3_db_handle(pStmt);
+ int rc = sqlite3_finalize(pStmt);
+ if( rc ){
+ fprintf(stderr, "finalize error: %s\n", sqlite3_errmsg(db));
+ exit(1);
+ }
+}
+
+/*
+** Execute zSql on database db. The SQL text is printed to standard
+** output. If an error occurs, print an error message and exit the
+** process.
+*/
+static void execSql(sqlite3 *db, const char *zSql){
+ sqlite3_stmt *pStmt;
+ if( !zSql ){
+ fprintf(stderr, "out of memory!\n");
+ exit(1);
+ }
+ printf("%s;\n", zSql);
+ if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
+ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
+ exit(1);
+ }
+ sqlite3_step(pStmt);
+ vacuumFinalize(pStmt);
+}
+
+/*
+** Execute zSql on database db. The zSql statement returns exactly
+** one column. Execute this return value as SQL on the same database.
+**
+** The zSql statement is printed on standard output prior to being
+** run. If any errors occur, an error is printed and the process
+** exits.
+*/
+static void execExecSql(sqlite3 *db, const char *zSql){
+ sqlite3_stmt *pStmt;
+ int rc;
+
+ printf("%s;\n", zSql);
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
+ exit(1);
+ }
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ execSql(db, (char*)sqlite3_column_text(pStmt, 0));
+ }
+ vacuumFinalize(pStmt);
+}
+
+
+int main(int argc, char **argv){
+ sqlite3 *db; /* Connection to the database file */
+ int rc; /* Return code from SQLite interface calls */
+ sqlite3_uint64 r; /* A random number */
+ const char *zDbToVacuum; /* Database to be vacuumed */
+ char *zBackupDb; /* Backup copy of the original database */
+ char *zTempDb; /* Temporary database */
+ char *zSql; /* An SQL statement */
+
+ if( argc!=2 ){
+ fprintf(stderr, "Usage: %s DATABASE\n", argv[0]);
+ return 1;
+ }
+
+ /* Identify the database file to be vacuumed and open it.
+ */
+ zDbToVacuum = argv[1];
+ printf("-- open database file \"%s\"\n", zDbToVacuum);
+ rc = sqlite3_open(zDbToVacuum, &db);
+ if( rc ){
+ fprintf(stderr, "%s: %s\n", zDbToVacuum, sqlite3_errstr(rc));
+ return 1;
+ }
+
+ /* Create names for two other files. zTempDb will be a new database
+ ** into which we construct a vacuumed copy of zDbToVacuum. zBackupDb
+ ** will be a new name for zDbToVacuum after it is vacuumed.
+ */
+ sqlite3_randomness(sizeof(r), &r);
+ zTempDb = sqlite3_mprintf("%s-vacuum-%016llx", zDbToVacuum, r);
+ zBackupDb = sqlite3_mprintf("%s-backup-%016llx", zDbToVacuum, r);
+
+ /* Attach the zTempDb database to the database connection.
+ */
+ zSql = sqlite3_mprintf("ATTACH '%q' AS vacuum_db;", zTempDb);
+ execSql(db, zSql);
+ sqlite3_free(zSql);
+
+ /* TODO:
+ ** Set the page_size and auto_vacuum mode for zTempDb here, if desired.
+ */
+
+ /* The vacuum will occur inside of a transaction. Set writable_schema
+ ** to ON so that we can directly update the sqlite_master table in the
+ ** zTempDb database.
+ */
+ execSql(db, "PRAGMA writable_schema=ON");
+ execSql(db, "BEGIN");
+
+
+ /* Query the schema of the main database. Create a mirror schema
+ ** in the temporary database.
+ */
+ execExecSql(db,
+ "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) "
+ " FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
+ " AND rootpage>0"
+ );
+ execExecSql(db,
+ "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)"
+ " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %'"
+ );
+ execExecSql(db,
+ "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) "
+ " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'"
+ );
+
+ /* Loop through the tables in the main database. For each, do
+ ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy
+ ** the contents to the temporary database.
+ */
+ execExecSql(db,
+ "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
+ "|| ' SELECT * FROM main.' || quote(name) "
+ "FROM main.sqlite_master "
+ "WHERE type = 'table' AND name!='sqlite_sequence' "
+ " AND rootpage>0"
+ );
+
+ /* Copy over the sequence table
+ */
+ execExecSql(db,
+ "SELECT 'DELETE FROM vacuum_db.' || quote(name) "
+ "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence'"
+ );
+ execExecSql(db,
+ "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
+ "|| ' SELECT * FROM main.' || quote(name) "
+ "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence'"
+ );
+
+ /* Copy the triggers, views, and virtual tables from the main database
+ ** over to the temporary database. None of these objects has any
+ ** associated storage, so all we have to do is copy their entries
+ ** from the SQLITE_MASTER table.
+ */
+ execSql(db,
+ "INSERT INTO vacuum_db.sqlite_master "
+ " SELECT type, name, tbl_name, rootpage, sql"
+ " FROM main.sqlite_master"
+ " WHERE type='view' OR type='trigger'"
+ " OR (type='table' AND rootpage=0)"
+ );
+
+ /* Commit the transaction and close the database
+ */
+ execSql(db, "COMMIT");
+ printf("-- close database\n");
+ sqlite3_close(db);
+
+
+ /* At this point, zDbToVacuum is unchanged. zTempDb contains a
+ ** vacuumed copy of zDbToVacuum. Rearrange filenames so that
+ ** zTempDb becomes thenew zDbToVacuum.
+ */
+ printf("-- rename \"%s\" to \"%s\"\n", zDbToVacuum, zBackupDb);
+ rename(zDbToVacuum, zBackupDb);
+ printf("-- rename \"%s\" to \"%s\"\n", zTempDb, zDbToVacuum);
+ rename(zTempDb, zDbToVacuum);
+
+ /* Release allocated memory */
+ sqlite3_free(zTempDb);
+ sqlite3_free(zBackupDb);
+ return 0;
+}
diff --git a/tool/lemon.c b/tool/lemon.c
index f63e76f..85e94f7 100644
--- a/tool/lemon.c
+++ b/tool/lemon.c
@@ -50,6 +50,107 @@ static char *msort(char*,char**,int(*)(const char*,const char*));
*/
#define lemonStrlen(X) ((int)strlen(X))
+/*
+** Compilers are starting to complain about the use of sprintf() and strcpy(),
+** saying they are unsafe. So we define our own versions of those routines too.
+**
+** There are three routines here: lemon_sprintf(), lemon_vsprintf(), and
+** lemon_addtext(). The first two are replacements for sprintf() and vsprintf().
+** The third is a helper routine for vsnprintf() that adds texts to the end of a
+** buffer, making sure the buffer is always zero-terminated.
+**
+** The string formatter is a minimal subset of stdlib sprintf() supporting only
+** a few simply conversions:
+**
+** %d
+** %s
+** %.*s
+**
+*/
+static void lemon_addtext(
+ char *zBuf, /* The buffer to which text is added */
+ int *pnUsed, /* Slots of the buffer used so far */
+ const char *zIn, /* Text to add */
+ int nIn, /* Bytes of text to add. -1 to use strlen() */
+ int iWidth /* Field width. Negative to left justify */
+){
+ if( nIn<0 ) for(nIn=0; zIn[nIn]; nIn++){}
+ while( iWidth>nIn ){ zBuf[(*pnUsed)++] = ' '; iWidth--; }
+ if( nIn==0 ) return;
+ memcpy(&zBuf[*pnUsed], zIn, nIn);
+ *pnUsed += nIn;
+ while( (-iWidth)>nIn ){ zBuf[(*pnUsed)++] = ' '; iWidth++; }
+ zBuf[*pnUsed] = 0;
+}
+static int lemon_vsprintf(char *str, const char *zFormat, va_list ap){
+ int i, j, k, c;
+ int nUsed = 0;
+ const char *z;
+ char zTemp[50];
+ str[0] = 0;
+ for(i=j=0; (c = zFormat[i])!=0; i++){
+ if( c=='%' ){
+ int iWidth = 0;
+ lemon_addtext(str, &nUsed, &zFormat[j], i-j, 0);
+ c = zFormat[++i];
+ if( isdigit(c) || (c=='-' && isdigit(zFormat[i+1])) ){
+ if( c=='-' ) i++;
+ while( isdigit(zFormat[i]) ) iWidth = iWidth*10 + zFormat[i++] - '0';
+ if( c=='-' ) iWidth = -iWidth;
+ c = zFormat[i];
+ }
+ if( c=='d' ){
+ int v = va_arg(ap, int);
+ if( v<0 ){
+ lemon_addtext(str, &nUsed, "-", 1, iWidth);
+ v = -v;
+ }else if( v==0 ){
+ lemon_addtext(str, &nUsed, "0", 1, iWidth);
+ }
+ k = 0;
+ while( v>0 ){
+ k++;
+ zTemp[sizeof(zTemp)-k] = (v%10) + '0';
+ v /= 10;
+ }
+ lemon_addtext(str, &nUsed, &zTemp[sizeof(zTemp)-k], k, iWidth);
+ }else if( c=='s' ){
+ z = va_arg(ap, const char*);
+ lemon_addtext(str, &nUsed, z, -1, iWidth);
+ }else if( c=='.' && memcmp(&zFormat[i], ".*s", 3)==0 ){
+ i += 2;
+ k = va_arg(ap, int);
+ z = va_arg(ap, const char*);
+ lemon_addtext(str, &nUsed, z, k, iWidth);
+ }else if( c=='%' ){
+ lemon_addtext(str, &nUsed, "%", 1, 0);
+ }else{
+ fprintf(stderr, "illegal format\n");
+ exit(1);
+ }
+ j = i+1;
+ }
+ }
+ lemon_addtext(str, &nUsed, &zFormat[j], i-j, 0);
+ return nUsed;
+}
+static int lemon_sprintf(char *str, const char *format, ...){
+ va_list ap;
+ int rc;
+ va_start(ap, format);
+ rc = lemon_vsprintf(str, format, ap);
+ va_end(ap);
+ return rc;
+}
+static void lemon_strcpy(char *dest, const char *src){
+ while( (*(dest++) = *(src++))!=0 ){}
+}
+static void lemon_strcat(char *dest, const char *src){
+ while( *dest ) dest++;
+ lemon_strcpy(dest, src);
+}
+
+
/* a few forward declarations... */
struct rule;
struct lemon;
@@ -1082,8 +1183,7 @@ static int resolve_conflict(
apx->type = SH_RESOLVED;
}else{
assert( spx->prec==spy->prec && spx->assoc==NONE );
- apy->type = SRCONFLICT;
- errcnt++;
+ apx->type = ERROR;
}
}else if( apx->type==REDUCE && apy->type==REDUCE ){
spx = apx->x.rp->precsym;
@@ -1367,7 +1467,7 @@ static void handle_D_option(char *z){
fprintf(stderr,"out of memory\n");
exit(1);
}
- strcpy(*paz, z);
+ lemon_strcpy(*paz, z);
for(z=*paz; *z && *z!='='; z++){}
*z = 0;
}
@@ -1378,7 +1478,7 @@ static void handle_T_option(char *z){
if( user_templatename==0 ){
memory_error();
}
- strcpy(user_templatename, z);
+ lemon_strcpy(user_templatename, z);
}
/* The main program. Parse the command line and do it... */
@@ -1447,12 +1547,15 @@ int main(int argc, char **argv)
}
/* Count and index the symbols of the grammar */
- lem.nsymbol = Symbol_count();
Symbol_new("{default}");
+ lem.nsymbol = Symbol_count();
lem.symbols = Symbol_arrayof();
- for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i;
- qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*), Symbolcmpp);
- for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i;
+ for(i=0; i<lem.nsymbol; i++) lem.symbols[i]->index = i;
+ qsort(lem.symbols,lem.nsymbol,sizeof(struct symbol*), Symbolcmpp);
+ for(i=0; i<lem.nsymbol; i++) lem.symbols[i]->index = i;
+ while( lem.symbols[i-1]->type==MULTITERMINAL ){ i--; }
+ assert( strcmp(lem.symbols[i-1]->name,"{default}")==0 );
+ lem.nsymbol = i - 1;
for(i=1; isupper(lem.symbols[i]->name[0]); i++);
lem.nterminal = i;
@@ -1940,7 +2043,9 @@ enum e_state {
WAITING_FOR_DESTRUCTOR_SYMBOL,
WAITING_FOR_DATATYPE_SYMBOL,
WAITING_FOR_FALLBACK_ID,
- WAITING_FOR_WILDCARD_ID
+ WAITING_FOR_WILDCARD_ID,
+ WAITING_FOR_CLASS_ID,
+ WAITING_FOR_CLASS_TOKEN
};
struct pstate {
char *filename; /* Name of the input file */
@@ -1950,6 +2055,7 @@ struct pstate {
struct lemon *gp; /* Global state vector */
enum e_state state; /* The state of the parser */
struct symbol *fallback; /* The fallback token */
+ struct symbol *tkclass; /* Token class symbol */
struct symbol *lhs; /* Left-hand side of current rule */
const char *lhsalias; /* Alias for the LHS */
int nrhs; /* Number of right-hand side symbols seen */
@@ -2254,6 +2360,8 @@ to follow the previous rule.");
psp->state = WAITING_FOR_FALLBACK_ID;
}else if( strcmp(x,"wildcard")==0 ){
psp->state = WAITING_FOR_WILDCARD_ID;
+ }else if( strcmp(x,"token_class")==0 ){
+ psp->state = WAITING_FOR_CLASS_ID;
}else{
ErrorMsg(psp->filename,psp->tokenlineno,
"Unknown declaration keyword: \"%%%s\".",x);
@@ -2347,7 +2455,7 @@ to follow the previous rule.");
for(z=psp->filename, nBack=0; *z; z++){
if( *z=='\\' ) nBack++;
}
- sprintf(zLine, "#line %d ", psp->tokenlineno);
+ lemon_sprintf(zLine, "#line %d ", psp->tokenlineno);
nLine = lemonStrlen(zLine);
n += nLine + lemonStrlen(psp->filename) + nBack;
}
@@ -2422,6 +2530,40 @@ to follow the previous rule.");
}
}
break;
+ case WAITING_FOR_CLASS_ID:
+ if( !islower(x[0]) ){
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "%%token_class must be followed by an identifier: ", x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else if( Symbol_find(x) ){
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "Symbol \"%s\" already used", x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ psp->tkclass = Symbol_new(x);
+ psp->tkclass->type = MULTITERMINAL;
+ psp->state = WAITING_FOR_CLASS_TOKEN;
+ }
+ break;
+ case WAITING_FOR_CLASS_TOKEN:
+ if( x[0]=='.' ){
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( isupper(x[0]) || ((x[0]=='|' || x[0]=='/') && isupper(x[1])) ){
+ struct symbol *msp = psp->tkclass;
+ msp->nsubsym++;
+ msp->subsym = (struct symbol **) realloc(msp->subsym,
+ sizeof(struct symbol*)*msp->nsubsym);
+ if( !isupper(x[0]) ) x++;
+ msp->subsym[msp->nsubsym-1] = Symbol_new(x);
+ }else{
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "%%token_class argument \"%s\" should be a token", x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ break;
case RESYNC_AFTER_RULE_ERROR:
/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE;
** break; */
@@ -2516,9 +2658,8 @@ void Parse(struct lemon *gp)
filesize = ftell(fp);
rewind(fp);
filebuf = (char *)malloc( filesize+1 );
- if( filebuf==0 ){
- ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.",
- filesize+1);
+ if( filesize>100000000 || filebuf==0 ){
+ ErrorMsg(ps.filename,0,"Input file too large.");
gp->errorcnt++;
fclose(fp);
return;
@@ -2716,10 +2857,10 @@ PRIVATE char *file_makename(struct lemon *lemp, const char *suffix)
fprintf(stderr,"Can't allocate space for a filename.\n");
exit(1);
}
- strcpy(name,lemp->filename);
+ lemon_strcpy(name,lemp->filename);
cp = strrchr(name,'.');
if( cp ) *cp = 0;
- strcat(name,suffix);
+ lemon_strcat(name,suffix);
return name;
}
@@ -2776,11 +2917,13 @@ void Reprint(struct lemon *lemp)
printf(" ::=");
for(i=0; i<rp->nrhs; i++){
sp = rp->rhs[i];
- printf(" %s", sp->name);
if( sp->type==MULTITERMINAL ){
+ printf(" %s", sp->subsym[0]->name);
for(j=1; j<sp->nsubsym; j++){
printf("|%s", sp->subsym[j]->name);
}
+ }else{
+ printf(" %s", sp->name);
}
/* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */
}
@@ -2802,11 +2945,13 @@ void ConfigPrint(FILE *fp, struct config *cfp)
if( i==cfp->dot ) fprintf(fp," *");
if( i==rp->nrhs ) break;
sp = rp->rhs[i];
- fprintf(fp," %s", sp->name);
if( sp->type==MULTITERMINAL ){
+ fprintf(fp," %s", sp->subsym[0]->name);
for(j=1; j<sp->nsubsym; j++){
fprintf(fp,"|%s",sp->subsym[j]->name);
}
+ }else{
+ fprintf(fp," %s", sp->name);
}
}
}
@@ -2916,7 +3061,7 @@ void ReportOutput(struct lemon *lemp)
while( cfp ){
char buf[20];
if( cfp->dot==cfp->rp->nrhs ){
- sprintf(buf,"(%d)",cfp->rp->index);
+ lemon_sprintf(buf,"(%d)",cfp->rp->index);
fprintf(fp," %5s ",buf);
}else{
fprintf(fp," ");
@@ -2981,7 +3126,7 @@ PRIVATE char *pathsearch(char *argv0, char *name, int modemask)
c = *cp;
*cp = 0;
path = (char *)malloc( lemonStrlen(argv0) + lemonStrlen(name) + 2 );
- if( path ) sprintf(path,"%s/%s",argv0,name);
+ if( path ) lemon_sprintf(path,"%s/%s",argv0,name);
*cp = c;
}else{
pathlist = getenv("PATH");
@@ -2990,13 +3135,13 @@ PRIVATE char *pathsearch(char *argv0, char *name, int modemask)
path = (char *)malloc( lemonStrlen(pathlist)+lemonStrlen(name)+2 );
if( (pathbuf != 0) && (path!=0) ){
pathbufptr = pathbuf;
- strcpy(pathbuf, pathlist);
+ lemon_strcpy(pathbuf, pathlist);
while( *pathbuf ){
cp = strchr(pathbuf,':');
if( cp==0 ) cp = &pathbuf[lemonStrlen(pathbuf)];
c = *cp;
*cp = 0;
- sprintf(path,"%s/%s",pathbuf,name);
+ lemon_sprintf(path,"%s/%s",pathbuf,name);
*cp = c;
if( c==0 ) pathbuf[0] = 0;
else pathbuf = &cp[1];
@@ -3087,9 +3232,9 @@ PRIVATE FILE *tplt_open(struct lemon *lemp)
cp = strrchr(lemp->filename,'.');
if( cp ){
- sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename);
+ lemon_sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename);
}else{
- sprintf(buf,"%s.lt",lemp->filename);
+ lemon_sprintf(buf,"%s.lt",lemp->filename);
}
if( access(buf,004)==0 ){
tpltname = buf;
@@ -3240,9 +3385,9 @@ PRIVATE char *append_str(const char *zText, int n, int p1, int p2){
while( n-- > 0 ){
c = *(zText++);
if( c=='%' && n>0 && zText[0]=='d' ){
- sprintf(zInt, "%d", p1);
+ lemon_sprintf(zInt, "%d", p1);
p1 = p2;
- strcpy(&z[used], zInt);
+ lemon_strcpy(&z[used], zInt);
used += lemonStrlen(&z[used]);
zText++;
n--;
@@ -3391,7 +3536,7 @@ void print_stack_union(
int maxdtlength; /* Maximum length of any ".datatype" field. */
char *stddt; /* Standardized name for a datatype */
int i,j; /* Loop counters */
- int hash; /* For hashing the name of a type */
+ unsigned hash; /* For hashing the name of a type */
const char *name; /* Name of the parser */
/* Allocate and initialize types[] and allocate stddt[] */
@@ -3458,7 +3603,7 @@ void print_stack_union(
break;
}
hash++;
- if( hash>=arraysize ) hash = 0;
+ if( hash>=(unsigned)arraysize ) hash = 0;
}
if( types[hash]==0 ){
sp->dtnum = hash + 1;
@@ -3467,7 +3612,7 @@ void print_stack_union(
fprintf(stderr,"Out of memory.\n");
exit(1);
}
- strcpy(types[hash],stddt);
+ lemon_strcpy(types[hash],stddt);
}
}
@@ -3553,9 +3698,11 @@ static void writeRuleText(FILE *out, struct rule *rp){
fprintf(out,"%s ::=", rp->lhs->name);
for(j=0; j<rp->nrhs; j++){
struct symbol *sp = rp->rhs[j];
- fprintf(out," %s", sp->name);
- if( sp->type==MULTITERMINAL ){
+ if( sp->type!=MULTITERMINAL ){
+ fprintf(out," %s", sp->name);
+ }else{
int k;
+ fprintf(out," %s", sp->subsym[0]->name);
for(k=1; k<sp->nsubsym; k++){
fprintf(out,"|%s",sp->subsym[k]->name);
}
@@ -3856,7 +4003,7 @@ void ReportTable(
/* Generate a table containing the symbolic name of every symbol
*/
for(i=0; i<lemp->nsymbol; i++){
- sprintf(line,"\"%s\",",lemp->symbols[i]->name);
+ lemon_sprintf(line,"\"%s\",",lemp->symbols[i]->name);
fprintf(out," %-15s",line);
if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; }
}
@@ -4023,7 +4170,8 @@ void ReportHeader(struct lemon *lemp)
if( in ){
int nextChar;
for(i=1; i<lemp->nterminal && fgets(line,LINESIZE,in); i++){
- sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ lemon_sprintf(pattern,"#define %s%-30s %3d\n",
+ prefix,lemp->symbols[i]->name,i);
if( strcmp(line,pattern) ) break;
}
nextChar = fgetc(in);
@@ -4036,7 +4184,7 @@ void ReportHeader(struct lemon *lemp)
out = file_open(lemp,".h","wb");
if( out ){
for(i=1; i<lemp->nterminal; i++){
- fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ fprintf(out,"#define %s%-30s %3d\n",prefix,lemp->symbols[i]->name,i);
}
fclose(out);
}
@@ -4234,10 +4382,10 @@ int SetUnion(char *s1, char *s2)
** Code for processing tables in the LEMON parser generator.
*/
-PRIVATE int strhash(const char *x)
+PRIVATE unsigned strhash(const char *x)
{
- int h = 0;
- while( *x) h = h*13 + *(x++);
+ unsigned h = 0;
+ while( *x ) h = h*13 + *(x++);
return h;
}
@@ -4253,7 +4401,7 @@ const char *Strsafe(const char *y)
if( y==0 ) return 0;
z = Strsafe_find(y);
if( z==0 && (cpy=(char *)malloc( lemonStrlen(y)+1 ))!=0 ){
- strcpy(cpy,y);
+ lemon_strcpy(cpy,y);
z = cpy;
Strsafe_insert(z);
}
@@ -4292,8 +4440,7 @@ void Strsafe_init(){
if( x1a ){
x1a->size = 1024;
x1a->count = 0;
- x1a->tbl = (x1node*)malloc(
- (sizeof(x1node) + sizeof(x1node*))*1024 );
+ x1a->tbl = (x1node*)calloc(1024, sizeof(x1node) + sizeof(x1node*));
if( x1a->tbl==0 ){
free(x1a);
x1a = 0;
@@ -4309,8 +4456,8 @@ void Strsafe_init(){
int Strsafe_insert(const char *data)
{
x1node *np;
- int h;
- int ph;
+ unsigned h;
+ unsigned ph;
if( x1a==0 ) return 0;
ph = strhash(data);
@@ -4330,8 +4477,7 @@ int Strsafe_insert(const char *data)
struct s_x1 array;
array.size = size = x1a->size*2;
array.count = x1a->count;
- array.tbl = (x1node*)malloc(
- (sizeof(x1node) + sizeof(x1node*))*size );
+ array.tbl = (x1node*)calloc(size, sizeof(x1node) + sizeof(x1node*));
if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
array.ht = (x1node**)&(array.tbl[size]);
for(i=0; i<size; i++) array.ht[i] = 0;
@@ -4364,7 +4510,7 @@ int Strsafe_insert(const char *data)
** if no such key. */
const char *Strsafe_find(const char *key)
{
- int h;
+ unsigned h;
x1node *np;
if( x1a==0 ) return 0;
@@ -4406,11 +4552,15 @@ struct symbol *Symbol_new(const char *x)
return sp;
}
-/* Compare two symbols for working purposes
+/* Compare two symbols for sorting purposes. Return negative,
+** zero, or positive if a is less then, equal to, or greater
+** than b.
**
** Symbols that begin with upper case letters (terminals or tokens)
** must sort before symbols that begin with lower case letters
-** (non-terminals). Other than that, the order does not matter.
+** (non-terminals). And MULTITERMINAL symbols (created using the
+** %token_class directive) must sort at the very end. Other than
+** that, the order does not matter.
**
** We find experimentally that leaving the symbols in their original
** order (the order they appeared in the grammar file) gives the
@@ -4418,12 +4568,11 @@ struct symbol *Symbol_new(const char *x)
*/
int Symbolcmpp(const void *_a, const void *_b)
{
- const struct symbol **a = (const struct symbol **) _a;
- const struct symbol **b = (const struct symbol **) _b;
- int i1 = (**a).index + 10000000*((**a).name[0]>'Z');
- int i2 = (**b).index + 10000000*((**b).name[0]>'Z');
- assert( i1!=i2 || strcmp((**a).name,(**b).name)==0 );
- return i1-i2;
+ const struct symbol *a = *(const struct symbol **) _a;
+ const struct symbol *b = *(const struct symbol **) _b;
+ int i1 = a->type==MULTITERMINAL ? 3 : a->name[0]>'Z' ? 2 : 1;
+ int i2 = b->type==MULTITERMINAL ? 3 : b->name[0]>'Z' ? 2 : 1;
+ return i1==i2 ? a->index - b->index : i1 - i2;
}
/* There is one instance of the following structure for each
@@ -4458,8 +4607,7 @@ void Symbol_init(){
if( x2a ){
x2a->size = 128;
x2a->count = 0;
- x2a->tbl = (x2node*)malloc(
- (sizeof(x2node) + sizeof(x2node*))*128 );
+ x2a->tbl = (x2node*)calloc(128, sizeof(x2node) + sizeof(x2node*));
if( x2a->tbl==0 ){
free(x2a);
x2a = 0;
@@ -4475,8 +4623,8 @@ void Symbol_init(){
int Symbol_insert(struct symbol *data, const char *key)
{
x2node *np;
- int h;
- int ph;
+ unsigned h;
+ unsigned ph;
if( x2a==0 ) return 0;
ph = strhash(key);
@@ -4496,8 +4644,7 @@ int Symbol_insert(struct symbol *data, const char *key)
struct s_x2 array;
array.size = size = x2a->size*2;
array.count = x2a->count;
- array.tbl = (x2node*)malloc(
- (sizeof(x2node) + sizeof(x2node*))*size );
+ array.tbl = (x2node*)calloc(size, sizeof(x2node) + sizeof(x2node*));
if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
array.ht = (x2node**)&(array.tbl[size]);
for(i=0; i<size; i++) array.ht[i] = 0;
@@ -4532,7 +4679,7 @@ int Symbol_insert(struct symbol *data, const char *key)
** if no such key. */
struct symbol *Symbol_find(const char *key)
{
- int h;
+ unsigned h;
x2node *np;
if( x2a==0 ) return 0;
@@ -4606,9 +4753,9 @@ PRIVATE int statecmp(struct config *a, struct config *b)
}
/* Hash a state */
-PRIVATE int statehash(struct config *a)
+PRIVATE unsigned statehash(struct config *a)
{
- int h=0;
+ unsigned h=0;
while( a ){
h = h*571 + a->rp->index*37 + a->dot;
a = a->bp;
@@ -4657,8 +4804,7 @@ void State_init(){
if( x3a ){
x3a->size = 128;
x3a->count = 0;
- x3a->tbl = (x3node*)malloc(
- (sizeof(x3node) + sizeof(x3node*))*128 );
+ x3a->tbl = (x3node*)calloc(128, sizeof(x3node) + sizeof(x3node*));
if( x3a->tbl==0 ){
free(x3a);
x3a = 0;
@@ -4674,8 +4820,8 @@ void State_init(){
int State_insert(struct state *data, struct config *key)
{
x3node *np;
- int h;
- int ph;
+ unsigned h;
+ unsigned ph;
if( x3a==0 ) return 0;
ph = statehash(key);
@@ -4695,8 +4841,7 @@ int State_insert(struct state *data, struct config *key)
struct s_x3 array;
array.size = size = x3a->size*2;
array.count = x3a->count;
- array.tbl = (x3node*)malloc(
- (sizeof(x3node) + sizeof(x3node*))*size );
+ array.tbl = (x3node*)calloc(size, sizeof(x3node) + sizeof(x3node*));
if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
array.ht = (x3node**)&(array.tbl[size]);
for(i=0; i<size; i++) array.ht[i] = 0;
@@ -4731,7 +4876,7 @@ int State_insert(struct state *data, struct config *key)
** if no such key. */
struct state *State_find(struct config *key)
{
- int h;
+ unsigned h;
x3node *np;
if( x3a==0 ) return 0;
@@ -4753,7 +4898,7 @@ struct state **State_arrayof()
int i,size;
if( x3a==0 ) return 0;
size = x3a->count;
- array = (struct state **)malloc( sizeof(struct state *)*size );
+ array = (struct state **)calloc(size, sizeof(struct state *));
if( array ){
for(i=0; i<size; i++) array[i] = x3a->tbl[i].data;
}
@@ -4761,9 +4906,9 @@ struct state **State_arrayof()
}
/* Hash a configuration */
-PRIVATE int confighash(struct config *a)
+PRIVATE unsigned confighash(struct config *a)
{
- int h=0;
+ unsigned h=0;
h = h*571 + a->rp->index*37 + a->dot;
return h;
}
@@ -4799,8 +4944,7 @@ void Configtable_init(){
if( x4a ){
x4a->size = 64;
x4a->count = 0;
- x4a->tbl = (x4node*)malloc(
- (sizeof(x4node) + sizeof(x4node*))*64 );
+ x4a->tbl = (x4node*)calloc(64, sizeof(x4node) + sizeof(x4node*));
if( x4a->tbl==0 ){
free(x4a);
x4a = 0;
@@ -4816,8 +4960,8 @@ void Configtable_init(){
int Configtable_insert(struct config *data)
{
x4node *np;
- int h;
- int ph;
+ unsigned h;
+ unsigned ph;
if( x4a==0 ) return 0;
ph = confighash(data);
@@ -4837,8 +4981,7 @@ int Configtable_insert(struct config *data)
struct s_x4 array;
array.size = size = x4a->size*2;
array.count = x4a->count;
- array.tbl = (x4node*)malloc(
- (sizeof(x4node) + sizeof(x4node*))*size );
+ array.tbl = (x4node*)calloc(size, sizeof(x4node) + sizeof(x4node*));
if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
array.ht = (x4node**)&(array.tbl[size]);
for(i=0; i<size; i++) array.ht[i] = 0;
diff --git a/tool/logest.c b/tool/logest.c
new file mode 100644
index 0000000..347fa68
--- /dev/null
+++ b/tool/logest.c
@@ -0,0 +1,170 @@
+/*
+** 2013-06-10
+**
+** 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 contains a simple command-line utility for converting from
+** integers and LogEst values and back again and for doing simple
+** arithmetic operations (multiple and add) on LogEst values.
+**
+** Usage:
+**
+** ./LogEst ARGS
+**
+** See the showHelp() routine for a description of valid arguments.
+** Examples:
+**
+** To convert 123 from LogEst to integer:
+**
+** ./LogEst ^123
+**
+** To convert 123456 from integer to LogEst:
+**
+** ./LogEst 123456
+**
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+#include <string.h>
+#include "sqlite3.h"
+
+typedef short int LogEst; /* 10 times log2() */
+
+LogEst logEstMultiply(LogEst a, LogEst b){ return a+b; }
+LogEst logEstAdd(LogEst a, LogEst b){
+ static const unsigned char x[] = {
+ 10, 10, /* 0,1 */
+ 9, 9, /* 2,3 */
+ 8, 8, /* 4,5 */
+ 7, 7, 7, /* 6,7,8 */
+ 6, 6, 6, /* 9,10,11 */
+ 5, 5, 5, /* 12-14 */
+ 4, 4, 4, 4, /* 15-18 */
+ 3, 3, 3, 3, 3, 3, /* 19-24 */
+ 2, 2, 2, 2, 2, 2, 2, /* 25-31 */
+ };
+ if( a<b ){ LogEst t = a; a = b; b = t; }
+ if( a>b+49 ) return a;
+ if( a>b+31 ) return a+1;
+ return a+x[a-b];
+}
+LogEst logEstFromInteger(sqlite3_uint64 x){
+ static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 };
+ LogEst y = 40;
+ if( x<8 ){
+ if( x<2 ) return 0;
+ while( x<8 ){ y -= 10; x <<= 1; }
+ }else{
+ while( x>255 ){ y += 40; x >>= 4; }
+ while( x>15 ){ y += 10; x >>= 1; }
+ }
+ return a[x&7] + y - 10;
+}
+static sqlite3_uint64 logEstToInt(LogEst x){
+ sqlite3_uint64 n;
+ if( x<10 ) return 1;
+ n = x%10;
+ x /= 10;
+ if( n>=5 ) n -= 2;
+ else if( n>=1 ) n -= 1;
+ if( x>=3 ) return (n+8)<<(x-3);
+ return (n+8)>>(3-x);
+}
+static LogEst logEstFromDouble(double x){
+ sqlite3_uint64 a;
+ LogEst e;
+ assert( sizeof(x)==8 && sizeof(a)==8 );
+ if( x<=0.0 ) return -32768;
+ if( x<0.01 ) return -logEstFromDouble(1.0/x);
+ if( x<1.0 ) return logEstFromDouble(100.0*x) - 66;
+ if( x<1024.0 ) return logEstFromInteger((sqlite3_uint64)(1024.0*x)) - 100;
+ if( x<=2000000000.0 ) return logEstFromInteger((sqlite3_uint64)x);
+ memcpy(&a, &x, 8);
+ e = (a>>52) - 1022;
+ return e*10;
+}
+
+int isInteger(const char *z){
+ while( z[0]>='0' && z[0]<='9' ) z++;
+ return z[0]==0;
+}
+
+int isFloat(const char *z){
+ char c;
+ while( ((c=z[0])>='0' && c<='9') || c=='.' || c=='E' || c=='e'
+ || c=='+' || c=='-' ) z++;
+ return z[0]==0;
+}
+
+static void showHelp(const char *zArgv0){
+ printf("Usage: %s ARGS...\n", zArgv0);
+ printf("Arguments:\n"
+ " NUM Convert NUM from integer to LogEst and push onto the stack\n"
+ " ^NUM Interpret NUM as a LogEst and push onto stack\n"
+ " x Multiple the top two elements of the stack\n"
+ " + Add the top two elements of the stack\n"
+ " dup Dupliate the top element on the stack\n"
+ " inv Take the reciprocal of the top of stack. N = 1/N.\n"
+ " log Find the LogEst of the number on top of stack\n"
+ " nlogn Compute NlogN where N is the top of stack\n"
+ );
+ exit(1);
+}
+
+int main(int argc, char **argv){
+ int i;
+ int n = 0;
+ LogEst a[100];
+ for(i=1; i<argc; i++){
+ const char *z = argv[i];
+ if( strcmp(z,"+")==0 ){
+ if( n>=2 ){
+ a[n-2] = logEstAdd(a[n-2],a[n-1]);
+ n--;
+ }
+ }else if( strcmp(z,"x")==0 ){
+ if( n>=2 ){
+ a[n-2] = logEstMultiply(a[n-2],a[n-1]);
+ n--;
+ }
+ }else if( strcmp(z,"dup")==0 ){
+ if( n>0 ){
+ a[n] = a[n-1];
+ n++;
+ }
+ }else if( strcmp(z,"log")==0 ){
+ if( n>0 ) a[n-1] = logEstFromInteger(a[n-1]) - 33;
+ }else if( strcmp(z,"nlogn")==0 ){
+ if( n>0 ) a[n-1] += logEstFromInteger(a[n-1]) - 33;
+ }else if( strcmp(z,"inv")==0 ){
+ if( n>0 ) a[n-1] = -a[n-1];
+ }else if( z[0]=='^' ){
+ a[n++] = atoi(z+1);
+ }else if( isInteger(z) ){
+ a[n++] = logEstFromInteger(atoi(z));
+ }else if( isFloat(z) && z[0]!='-' ){
+ a[n++] = logEstFromDouble(atof(z));
+ }else{
+ showHelp(argv[0]);
+ }
+ }
+ for(i=n-1; i>=0; i--){
+ if( a[i]<-40 ){
+ printf("%5d (%f)\n", a[i], 1.0/(double)logEstToInt(-a[i]));
+ }else if( a[i]<10 ){
+ printf("%5d (%f)\n", a[i], logEstToInt(a[i]+100)/1024.0);
+ }else{
+ sqlite3_uint64 x = logEstToInt(a[i]+100)*100/1024;
+ printf("%5d (%lld.%02lld)\n", a[i], x/100, x%100);
+ }
+ }
+ return 0;
+}
diff --git a/tool/mkautoconfamal.sh b/tool/mkautoconfamal.sh
new file mode 100644
index 0000000..c13f7c9
--- /dev/null
+++ b/tool/mkautoconfamal.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+# This script is used to build the amalgamation autoconf package.
+# It assumes the following:
+#
+# 1. The files "sqlite3.c", "sqlite3.h" and "sqlite3ext.h"
+# are available in the current directory.
+#
+# 2. Variable $TOP is set to the full path of the root directory
+# of the SQLite source tree.
+#
+# 3. There is nothing of value in the ./mkpkg_tmp_dir directory.
+# This is important, as the script executes "rm -rf ./mkpkg_tmp_dir".
+#
+
+
+# Bail out of the script if any command returns a non-zero exit
+# status. Or if the script tries to use an unset variable. These
+# may fail for old /bin/sh interpreters.
+#
+set -e
+set -u
+
+TMPSPACE=./mkpkg_tmp_dir
+VERSION=`cat $TOP/VERSION`
+
+# Set global variable $ARTIFACT to the "3xxyyzz" string incorporated
+# into artifact filenames. And $VERSION2 to the "3.x.y[.z]" form.
+xx=`echo $VERSION|sed 's/3\.\([0-9]*\)\..*/\1/'`
+yy=`echo $VERSION|sed 's/3\.[^.]*\.\([0-9]*\).*/\1/'`
+zz=0
+set +e
+ zz=`echo $VERSION|sed 's/3\.[^.]*\.[^.]*\.\([0-9]*\).*/\1/'|grep -v '\.'`
+set -e
+ARTIFACT=`printf "3%.2d%.2d%.2d" $xx $yy $zz`
+
+rm -rf $TMPSPACE
+cp -R $TOP/autoconf $TMPSPACE
+
+cp sqlite3.c $TMPSPACE
+cp sqlite3.h $TMPSPACE
+cp sqlite3ext.h $TMPSPACE
+cp $TOP/sqlite3.1 $TMPSPACE
+cp $TOP/sqlite3.pc.in $TMPSPACE
+cp $TOP/src/shell.c $TMPSPACE
+
+chmod 755 $TMPSPACE/install-sh
+chmod 755 $TMPSPACE/missing
+chmod 755 $TMPSPACE/depcomp
+chmod 755 $TMPSPACE/config.sub
+chmod 755 $TMPSPACE/config.guess
+
+cat $TMPSPACE/configure.ac |
+sed "s/AC_INIT(sqlite, .*, http:\/\/www.sqlite.org)/AC_INIT(sqlite, $VERSION, http:\/\/www.sqlite.org)/" > $TMPSPACE/tmp
+mv $TMPSPACE/tmp $TMPSPACE/configure.ac
+
+cd $TMPSPACE
+aclocal
+autoconf
+automake
+
+mkdir -p tea/generic
+echo "#ifdef USE_SYSTEM_SQLITE" > tea/generic/tclsqlite3.c
+echo "# include <sqlite3.h>" >> tea/generic/tclsqlite3.c
+echo "#else" >> tea/generic/tclsqlite3.c
+echo "#include \"../../sqlite3.c\"" >> tea/generic/tclsqlite3.c
+echo "#endif" >> tea/generic/tclsqlite3.c
+cat $TOP/src/tclsqlite.c >> tea/generic/tclsqlite3.c
+
+cat tea/configure.in |
+ sed "s/AC_INIT(\[sqlite\], .*)/AC_INIT([sqlite], [$VERSION])/" > tmp
+mv tmp tea/configure.in
+
+cd tea
+autoconf
+rm -rf autom4te.cache
+
+cd ../
+./configure && make dist
+tar -xzf sqlite-$VERSION.tar.gz
+mv sqlite-$VERSION sqlite-autoconf-$ARTIFACT
+tar -czf sqlite-autoconf-$ARTIFACT.tar.gz sqlite-autoconf-$ARTIFACT
+mv sqlite-autoconf-$ARTIFACT.tar.gz ..
+
diff --git a/tool/mkkeywordhash.c b/tool/mkkeywordhash.c
index 4e5ba8f..721611f 100644
--- a/tool/mkkeywordhash.c
+++ b/tool/mkkeywordhash.c
@@ -138,6 +138,11 @@ struct Keyword {
#else
# define AUTOVACUUM 0x00020000
#endif
+#ifdef SQLITE_OMIT_CTE
+# define CTE 0
+#else
+# define CTE 0x00040000
+#endif
/*
** These are the keywords
@@ -234,6 +239,7 @@ static Keyword aKeywordTable[] = {
{ "PRIMARY", "TK_PRIMARY", ALWAYS },
{ "QUERY", "TK_QUERY", EXPLAIN },
{ "RAISE", "TK_RAISE", TRIGGER },
+ { "RECURSIVE", "TK_RECURSIVE", CTE },
{ "REFERENCES", "TK_REFERENCES", FKEY },
{ "REGEXP", "TK_LIKE_KW", ALWAYS },
{ "REINDEX", "TK_REINDEX", REINDEX },
@@ -262,6 +268,8 @@ static Keyword aKeywordTable[] = {
{ "VALUES", "TK_VALUES", ALWAYS },
{ "VIEW", "TK_VIEW", VIEW },
{ "VIRTUAL", "TK_VIRTUAL", VTAB },
+ { "WITH", "TK_WITH", CTE },
+ { "WITHOUT", "TK_WITHOUT", ALWAYS },
{ "WHEN", "TK_WHEN", ALWAYS },
{ "WHERE", "TK_WHERE", ALWAYS },
};
@@ -362,7 +370,7 @@ int main(int argc, char **argv){
Keyword *p = &aKeywordTable[i];
p->len = (int)strlen(p->zName);
assert( p->len<sizeof(p->zOrigName) );
- strcpy(p->zOrigName, p->zName);
+ memcpy(p->zOrigName, p->zName, p->len+1);
totalLen += p->len;
p->hash = (UpperToLower[(int)p->zName[0]]*4) ^
(UpperToLower[(int)p->zName[p->len-1]]*3) ^ p->len;
diff --git a/tool/mkpragmatab.tcl b/tool/mkpragmatab.tcl
new file mode 100644
index 0000000..28a1e46
--- /dev/null
+++ b/tool/mkpragmatab.tcl
@@ -0,0 +1,434 @@
+#!/usr/bin/tclsh
+#
+# Run this script to generate the pragma name lookup table C code.
+#
+# To add new pragmas, first add the name and other relevant attributes
+# of the pragma to the "pragma_def" object below. Then run this script
+# to generate the C-code for the lookup table and copy/paste the output
+# of this script into the appropriate spot in the pragma.c source file.
+# Then add the extra "case PragTyp_XXXXX:" and subsequent code for the
+# new pragma.
+#
+
+set pragma_def {
+ NAME: full_column_names
+ TYPE: FLAG
+ ARG: SQLITE_FullColNames
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: short_column_names
+ TYPE: FLAG
+ ARG: SQLITE_ShortColNames
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: count_changes
+ TYPE: FLAG
+ ARG: SQLITE_CountRows
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: empty_result_callbacks
+ TYPE: FLAG
+ ARG: SQLITE_NullCallback
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: legacy_file_format
+ TYPE: FLAG
+ ARG: SQLITE_LegacyFileFmt
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: fullfsync
+ TYPE: FLAG
+ ARG: SQLITE_FullFSync
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: checkpoint_fullfsync
+ TYPE: FLAG
+ ARG: SQLITE_CkptFullFSync
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: cache_spill
+ TYPE: FLAG
+ ARG: SQLITE_CacheSpill
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: reverse_unordered_selects
+ TYPE: FLAG
+ ARG: SQLITE_ReverseOrder
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: query_only
+ TYPE: FLAG
+ ARG: SQLITE_QueryOnly
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: automatic_index
+ TYPE: FLAG
+ ARG: SQLITE_AutoIndex
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: !defined(SQLITE_OMIT_AUTOMATIC_INDEX)
+
+ NAME: sql_trace
+ TYPE: FLAG
+ ARG: SQLITE_SqlTrace
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: vdbe_listing
+ TYPE: FLAG
+ ARG: SQLITE_VdbeListing
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: vdbe_trace
+ TYPE: FLAG
+ ARG: SQLITE_VdbeTrace
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: vdbe_addoptrace
+ TYPE: FLAG
+ ARG: SQLITE_VdbeAddopTrace
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: vdbe_debug
+ TYPE: FLAG
+ ARG: SQLITE_SqlTrace|SQLITE_VdbeListing|SQLITE_VdbeTrace
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: vdbe_eqp
+ TYPE: FLAG
+ ARG: SQLITE_VdbeEQP
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: ignore_check_constraints
+ TYPE: FLAG
+ ARG: SQLITE_IgnoreChecks
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: !defined(SQLITE_OMIT_CHECK)
+
+ NAME: writable_schema
+ TYPE: FLAG
+ ARG: SQLITE_WriteSchema|SQLITE_RecoveryMode
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: read_uncommitted
+ TYPE: FLAG
+ ARG: SQLITE_ReadUncommitted
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: recursive_triggers
+ TYPE: FLAG
+ ARG: SQLITE_RecTriggers
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: foreign_keys
+ TYPE: FLAG
+ ARG: SQLITE_ForeignKeys
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+
+ NAME: defer_foreign_keys
+ TYPE: FLAG
+ ARG: SQLITE_DeferFKs
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+
+ NAME: default_cache_size
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
+
+ NAME: page_size
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: secure_delete
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: page_count
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: max_page_count
+ TYPE: PAGE_COUNT
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: locking_mode
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: journal_mode
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: journal_size_limit
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: cache_size
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: mmap_size
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: auto_vacuum
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_AUTOVACUUM)
+
+ NAME: incremental_vacuum
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_AUTOVACUUM)
+
+ NAME: temp_store
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: temp_store_directory
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: data_store_directory
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_OS_WIN
+
+ NAME: lock_proxy_file
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE
+
+ NAME: synchronous
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: table_info
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: stats
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: index_info
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: index_list
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: database_list
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: collation_list
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: foreign_key_list
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_FOREIGN_KEY)
+
+ NAME: foreign_key_check
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+
+ NAME: parser_trace
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: case_sensitive_like
+
+ NAME: integrity_check
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_INTEGRITY_CHECK)
+
+ NAME: quick_check
+ TYPE: INTEGRITY_CHECK
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_INTEGRITY_CHECK)
+
+ NAME: encoding
+ IF: !defined(SQLITE_OMIT_UTF16)
+
+ NAME: schema_version
+ TYPE: HEADER_VALUE
+ IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+
+ NAME: user_version
+ TYPE: HEADER_VALUE
+ IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+
+ NAME: freelist_count
+ TYPE: HEADER_VALUE
+ IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+
+ NAME: application_id
+ TYPE: HEADER_VALUE
+ IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+
+ NAME: compile_options
+ IF: !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
+
+ NAME: wal_checkpoint
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_WAL)
+
+ NAME: wal_autocheckpoint
+ IF: !defined(SQLITE_OMIT_WAL)
+
+ NAME: shrink_memory
+
+ NAME: busy_timeout
+
+ NAME: lock_status
+ IF: defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+
+ NAME: key
+ IF: defined(SQLITE_HAS_CODEC)
+
+ NAME: rekey
+ IF: defined(SQLITE_HAS_CODEC)
+
+ NAME: hexkey
+ IF: defined(SQLITE_HAS_CODEC)
+
+ NAME: hexrekey
+ TYPE: HEXKEY
+ IF: defined(SQLITE_HAS_CODEC)
+
+ NAME: activate_extensions
+ IF: defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
+
+ NAME: soft_heap_limit
+}
+fconfigure stdout -translation lf
+set name {}
+set type {}
+set if {}
+set flags {}
+set arg 0
+proc record_one {} {
+ global name type if arg allbyname typebyif flags
+ if {$name==""} return
+ set allbyname($name) [list $type $arg $if $flags]
+ set name {}
+ set type {}
+ set if {}
+ set flags {}
+ set arg 0
+}
+foreach line [split $pragma_def \n] {
+ set line [string trim $line]
+ if {$line==""} continue
+ foreach {id val} [split $line :] break
+ set val [string trim $val]
+ if {$id=="NAME"} {
+ record_one
+ set name $val
+ set type [string toupper $val]
+ } elseif {$id=="TYPE"} {
+ set type $val
+ } elseif {$id=="ARG"} {
+ set arg $val
+ } elseif {$id=="IF"} {
+ lappend if $val
+ } elseif {$id=="FLAG"} {
+ foreach term [split $val] {
+ lappend flags $term
+ set allflags($term) 1
+ }
+ } else {
+ error "bad pragma_def line: $line"
+ }
+}
+record_one
+set allnames [lsort [array names allbyname]]
+
+# Generate #defines for all pragma type names. Group the pragmas that are
+# omit in default builds (defined(SQLITE_DEBUG) and defined(SQLITE_HAS_CODEC))
+# at the end.
+#
+set pnum 0
+foreach name $allnames {
+ set type [lindex $allbyname($name) 0]
+ if {[info exists seentype($type)]} continue
+ set if [lindex $allbyname($name) 2]
+ if {[regexp SQLITE_DEBUG $if] || [regexp SQLITE_HAS_CODEC $if]} continue
+ set seentype($type) 1
+ puts [format {#define %-35s %4d} PragTyp_$type $pnum]
+ incr pnum
+}
+foreach name $allnames {
+ set type [lindex $allbyname($name) 0]
+ if {[info exists seentype($type)]} continue
+ set if [lindex $allbyname($name) 2]
+ if {[regexp SQLITE_DEBUG $if]} continue
+ set seentype($type) 1
+ puts [format {#define %-35s %4d} PragTyp_$type $pnum]
+ incr pnum
+}
+foreach name $allnames {
+ set type [lindex $allbyname($name) 0]
+ if {[info exists seentype($type)]} continue
+ set seentype($type) 1
+ puts [format {#define %-35s %4d} PragTyp_$type $pnum]
+ incr pnum
+}
+
+# Generate #defines for flags
+#
+set fv 1
+foreach f [lsort [array names allflags]] {
+ puts [format {#define PragFlag_%-20s 0x%02x} $f $fv]
+ set fv [expr {$fv*2}]
+}
+
+# Generate the lookup table
+#
+puts "static const struct sPragmaNames \173"
+puts " const char *const zName; /* Name of pragma */"
+puts " u8 ePragTyp; /* PragTyp_XXX value */"
+puts " u8 mPragFlag; /* Zero or more PragFlag_XXX values */"
+puts " u32 iArg; /* Extra argument */"
+puts "\175 aPragmaNames\[\] = \173"
+
+set current_if {}
+set spacer [format { %26s } {}]
+foreach name $allnames {
+ foreach {type arg if flag} $allbyname($name) break
+ if {$if!=$current_if} {
+ if {$current_if!=""} {
+ foreach this_if $current_if {
+ puts "#endif"
+ }
+ }
+ set current_if $if
+ if {$current_if!=""} {
+ foreach this_if $current_if {
+ puts "#if $this_if"
+ }
+ }
+ }
+ set typex [format PragTyp_%-23s $type,]
+ if {$flag==""} {
+ set flagx "0"
+ } else {
+ set flagx PragFlag_[join $flag {|PragFlag_}]
+ }
+ puts " \173 /* zName: */ \"$name\","
+ puts " /* ePragTyp: */ PragTyp_$type,"
+ puts " /* ePragFlag: */ $flagx,"
+ puts " /* iArg: */ $arg \175,"
+}
+if {$current_if!=""} {
+ foreach this_if $current_if {
+ puts "#endif"
+ }
+}
+puts "\175;"
+
+# count the number of pragmas, for information purposes
+#
+set allcnt 0
+set dfltcnt 0
+foreach name $allnames {
+ incr allcnt
+ set if [lindex $allbyname($name) 2]
+ if {[regexp {^defined} $if] || [regexp {[^!]defined} $if]} continue
+ incr dfltcnt
+}
+puts "/* Number of pragmas: $dfltcnt on by default, $allcnt total. */"
diff --git a/tool/mksqlite3c-noext.tcl b/tool/mksqlite3c-noext.tcl
index 017ad62..ecb9cb0 100644
--- a/tool/mksqlite3c-noext.tcl
+++ b/tool/mksqlite3c-noext.tcl
@@ -99,6 +99,8 @@ foreach hdr {
mutex.h
opcodes.h
os_common.h
+ os_setup.h
+ os_win.h
os.h
pager.h
parse.h
diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl
index 07c01ef..c14be20 100644
--- a/tool/mksqlite3c.tcl
+++ b/tool/mksqlite3c.tcl
@@ -105,6 +105,8 @@ foreach hdr {
mutex.h
opcodes.h
os_common.h
+ os_setup.h
+ os_win.h
os.h
pager.h
parse.h
@@ -118,6 +120,7 @@ foreach hdr {
vdbe.h
vdbeInt.h
wal.h
+ whereInt.h
} {
set available_hdr($hdr) 1
}
@@ -139,7 +142,7 @@ proc section_comment {text} {
# Read the source file named $filename and write it into the
# sqlite3.c output file. If any #include statements are seen,
-# process them approprately.
+# process them appropriately.
#
proc copy_file {filename} {
global seen_hdr available_hdr out addstatic linemacros
@@ -169,10 +172,18 @@ proc copy_file {filename} {
if {$linemacros} {puts $out "#line [expr {$ln+1}] \"$filename\""}
}
} elseif {![info exists seen_hdr($hdr)]} {
- set seen_hdr($hdr) 1
+ if {![regexp {/\*\s+amalgamator:\s+dontcache\s+\*/} $line]} {
+ set seen_hdr($hdr) 1
+ }
+ puts $out $line
+ } elseif {[regexp {/\*\s+amalgamator:\s+keep\s+\*/} $line]} {
+ # This include file must be kept because there was a "keep"
+ # directive inside of a line comment.
puts $out $line
} else {
- puts $out "/* $line */"
+ # Comment out the entire line, replacing any nested comment
+ # begin/end markers with the harmless substring "**".
+ puts $out "/* [string map [list /* ** */ **] $line] */"
}
} elseif {[regexp {^#ifdef __cplusplus} $line]} {
puts $out "#if 0"
diff --git a/tool/mksqlite3internalh.tcl b/tool/mksqlite3internalh.tcl
index 406ef5c..7e92b3a 100644
--- a/tool/mksqlite3internalh.tcl
+++ b/tool/mksqlite3internalh.tcl
@@ -60,6 +60,8 @@ foreach hdr {
keywordhash.h
opcodes.h
os_common.h
+ os_setup.h
+ os_win.h
os.h
pager.h
parse.h
diff --git a/tool/mkvsix.tcl b/tool/mkvsix.tcl
index e9f1f81..208ce2b 100644
--- a/tool/mkvsix.tcl
+++ b/tool/mkvsix.tcl
@@ -65,12 +65,16 @@
# argument is optional and if present must contain the name of the directory
# containing the root of the source tree for SQLite. The third argument is
# optional and if present must contain the flavor the VSIX package to build.
-# Currently, the only supported package flavors are "WinRT" and "WP80". The
-# fourth argument is optional and if present must be a string containing a list
-# of platforms to include in the VSIX package. The format of the platform list
-# string is "platform1,platform2,platform3". Typically, when on Windows, this
-# script is executed using commands similar to the following from a normal
-# Windows command prompt:
+# Currently, the only supported package flavors are "WinRT", "WinRT81", "WP80",
+# "WP81", and "Win32". The fourth argument is optional and if present must be
+# a string containing a list of platforms to include in the VSIX package. The
+# platform list is "platform1,platform2,platform3". The fifth argument is
+# optional and if present must contain the version of Visual Studio required by
+# the package. Currently, the only supported versions are "2012" and "2013".
+# The package flavors "WinRT81" and "WP81" are only supported when the Visual
+# Studio version is "2013". Typically, when on Windows, this script is
+# executed using commands similar to the following from a normal Windows
+# command prompt:
#
# CD /D C:\dev\sqlite\core
# tclsh85 tool\mkvsix.tcl C:\Temp
@@ -100,7 +104,7 @@ proc fail { {error ""} {usage false} } {
puts stdout "usage:\
[file tail [info nameofexecutable]]\
[file tail [info script]] <binaryDirectory> \[sourceDirectory\]\
-\[packageFlavor\] \[platformNames\]"
+\[packageFlavor\] \[platformNames\] \[vsVersion\]"
exit 1
}
@@ -170,13 +174,81 @@ proc writeFile { fileName data } {
return ""
}
-proc substFile { fileName } {
+proc getMinVsVersionXmlChunk { vsVersion } {
+ switch -exact $vsVersion {
+ 2012 {
+ return [appendArgs \
+ "\r\n " {MinVSVersion="11.0"}]
+ }
+ 2013 {
+ return [appendArgs \
+ "\r\n " {MinVSVersion="12.0"}]
+ }
+ default {
+ return ""
+ }
+ }
+}
+
+proc getMaxPlatformVersionXmlChunk { packageFlavor vsVersion } {
#
- # NOTE: Performs all Tcl command, variable, and backslash substitutions in
- # the specified file and then rewrites the contents of that same file
- # with the substituted data.
+ # NOTE: Only Visual Studio 2013 supports this SDK manifest attribute.
#
- return [writeFile $fileName [uplevel 1 [list subst [readFile $fileName]]]]
+ if {![string equal $vsVersion 2013]} then {
+ return ""
+ }
+
+ switch -exact $packageFlavor {
+ WinRT {
+ return [appendArgs \
+ "\r\n " {MaxPlatformVersion="8.0"}]
+ }
+ WinRT81 {
+ return [appendArgs \
+ "\r\n " {MaxPlatformVersion="8.1"}]
+ }
+ WP80 {
+ return [appendArgs \
+ "\r\n " {MaxPlatformVersion="8.0"}]
+ }
+ WP81 {
+ return [appendArgs \
+ "\r\n " {MaxPlatformVersion="8.1"}]
+ }
+ default {
+ return ""
+ }
+ }
+}
+
+proc getExtraFileListXmlChunk { packageFlavor vsVersion } {
+ #
+ # NOTE: Windows Phone 8.0 does not require any extra attributes in its VSIX
+ # package SDK manifests; however, it appears that Windows Phone 8.1
+ # does.
+ #
+ if {[string equal $packageFlavor WP80]} then {
+ return ""
+ }
+
+ set appliesTo [expr {[string equal $packageFlavor Win32] ? \
+ "VisualC" : "WindowsAppContainer"}]
+
+ switch -exact $vsVersion {
+ 2012 {
+ return [appendArgs \
+ "\r\n " AppliesTo=\" $appliesTo \" \
+ "\r\n " {DependsOn="Microsoft.VCLibs, version=11.0"}]
+ }
+ 2013 {
+ return [appendArgs \
+ "\r\n " AppliesTo=\" $appliesTo \" \
+ "\r\n " {DependsOn="Microsoft.VCLibs, version=12.0"}]
+ }
+ default {
+ return ""
+ }
+ }
}
proc replaceFileNameTokens { fileName name buildName platformName } {
@@ -188,6 +260,15 @@ proc replaceFileNameTokens { fileName name buildName platformName } {
<name> $name] $fileName]
}
+proc substFile { fileName } {
+ #
+ # NOTE: Performs all Tcl command, variable, and backslash substitutions in
+ # the specified file and then rewrites the contents of that same file
+ # with the substituted data.
+ #
+ return [writeFile $fileName [uplevel 1 [list subst [readFile $fileName]]]]
+}
+
#
# NOTE: This is the entry point for this script.
#
@@ -206,7 +287,7 @@ set rootName [file rootname [file tail $script]]
# NOTE: Process and verify all the command line arguments.
#
set argc [llength $argv]
-if {$argc < 1 || $argc > 4} then {fail}
+if {$argc < 1 || $argc > 5} then {fail}
set binaryDirectory [lindex $argv 0]
@@ -251,34 +332,123 @@ if {[string length $packageFlavor] == 0} then {
fail "invalid package flavor"
}
-if {[string equal -nocase $packageFlavor WinRT]} then {
- set shortName SQLite.WinRT
- set displayName "SQLite for Windows Runtime"
- set targetPlatformIdentifier Windows
- set extraSdkPath ""
- set extraFileListAttributes [appendArgs \
- "\r\n " {AppliesTo="WindowsAppContainer"} \
- "\r\n " {DependsOn="Microsoft.VCLibs, version=11.0"}]
-} elseif {[string equal -nocase $packageFlavor WP80]} then {
- set shortName SQLite.WP80
- set displayName "SQLite for Windows Phone"
- set targetPlatformIdentifier "Windows Phone"
- set extraSdkPath "\\..\\$targetPlatformIdentifier"
- set extraFileListAttributes ""
-} else {
- fail "unsupported package flavor, must be \"WinRT\" or \"WP80\""
-}
-
if {$argc >= 4} then {
set platformNames [list]
foreach platformName [split [lindex $argv 3] ", "] {
+ set platformName [string trim $platformName]
+
if {[string length $platformName] > 0} then {
lappend platformNames $platformName
}
}
}
+if {$argc >= 5} then {
+ set vsVersion [lindex $argv 4]
+} else {
+ set vsVersion 2012
+}
+
+if {[string length $vsVersion] == 0} then {
+ fail "invalid Visual Studio version"
+}
+
+if {![string equal $vsVersion 2012] && ![string equal $vsVersion 2013]} then {
+ fail [appendArgs \
+ "unsupported Visual Studio version, must be one of: " \
+ [list 2012 2013]]
+}
+
+set shortNames(WinRT,2012) SQLite.WinRT
+set shortNames(WinRT,2013) SQLite.WinRT.2013
+set shortNames(WinRT81,2013) SQLite.WinRT81
+set shortNames(WP80,2012) SQLite.WP80
+set shortNames(WP80,2013) SQLite.WP80.2013
+set shortNames(WP81,2013) SQLite.WP81
+set shortNames(Win32,2012) SQLite.Win32
+set shortNames(Win32,2013) SQLite.Win32.2013
+
+set displayNames(WinRT,2012) "SQLite for Windows Runtime"
+set displayNames(WinRT,2013) "SQLite for Windows Runtime"
+set displayNames(WinRT81,2013) "SQLite for Windows Runtime (Windows 8.1)"
+set displayNames(WP80,2012) "SQLite for Windows Phone"
+set displayNames(WP80,2013) "SQLite for Windows Phone"
+set displayNames(WP81,2013) "SQLite for Windows Phone 8.1"
+set displayNames(Win32,2012) "SQLite for Windows"
+set displayNames(Win32,2013) "SQLite for Windows"
+
+if {[string equal $packageFlavor WinRT]} then {
+ set shortName $shortNames($packageFlavor,$vsVersion)
+ set displayName $displayNames($packageFlavor,$vsVersion)
+ set targetPlatformIdentifier Windows
+ set targetPlatformVersion v8.0
+ set minVsVersion [getMinVsVersionXmlChunk $vsVersion]
+ set maxPlatformVersion \
+ [getMaxPlatformVersionXmlChunk $packageFlavor $vsVersion]
+ set extraSdkPath ""
+ set extraFileListAttributes \
+ [getExtraFileListXmlChunk $packageFlavor $vsVersion]
+} elseif {[string equal $packageFlavor WinRT81]} then {
+ if {$vsVersion ne "2013"} then {
+ fail [appendArgs \
+ "unsupported combination, package flavor " $packageFlavor \
+ " is only supported with Visual Studio 2013"]
+ }
+ set shortName $shortNames($packageFlavor,$vsVersion)
+ set displayName $displayNames($packageFlavor,$vsVersion)
+ set targetPlatformIdentifier Windows
+ set targetPlatformVersion v8.1
+ set minVsVersion [getMinVsVersionXmlChunk $vsVersion]
+ set maxPlatformVersion \
+ [getMaxPlatformVersionXmlChunk $packageFlavor $vsVersion]
+ set extraSdkPath ""
+ set extraFileListAttributes \
+ [getExtraFileListXmlChunk $packageFlavor $vsVersion]
+} elseif {[string equal $packageFlavor WP80]} then {
+ set shortName $shortNames($packageFlavor,$vsVersion)
+ set displayName $displayNames($packageFlavor,$vsVersion)
+ set targetPlatformIdentifier "Windows Phone"
+ set targetPlatformVersion v8.0
+ set minVsVersion [getMinVsVersionXmlChunk $vsVersion]
+ set maxPlatformVersion \
+ [getMaxPlatformVersionXmlChunk $packageFlavor $vsVersion]
+ set extraSdkPath "\\..\\$targetPlatformIdentifier"
+ set extraFileListAttributes \
+ [getExtraFileListXmlChunk $packageFlavor $vsVersion]
+} elseif {[string equal $packageFlavor WP81]} then {
+ if {$vsVersion ne "2013"} then {
+ fail [appendArgs \
+ "unsupported combination, package flavor " $packageFlavor \
+ " is only supported with Visual Studio 2013"]
+ }
+ set shortName $shortNames($packageFlavor,$vsVersion)
+ set displayName $displayNames($packageFlavor,$vsVersion)
+ set targetPlatformIdentifier WindowsPhoneApp
+ set targetPlatformVersion v8.1
+ set minVsVersion [getMinVsVersionXmlChunk $vsVersion]
+ set maxPlatformVersion \
+ [getMaxPlatformVersionXmlChunk $packageFlavor $vsVersion]
+ set extraSdkPath "\\..\\$targetPlatformIdentifier"
+ set extraFileListAttributes \
+ [getExtraFileListXmlChunk $packageFlavor $vsVersion]
+} elseif {[string equal $packageFlavor Win32]} then {
+ set shortName $shortNames($packageFlavor,$vsVersion)
+ set displayName $displayNames($packageFlavor,$vsVersion)
+ set targetPlatformIdentifier Windows
+ set targetPlatformVersion v8.0
+ set minVsVersion [getMinVsVersionXmlChunk $vsVersion]
+ set maxPlatformVersion \
+ [getMaxPlatformVersionXmlChunk $packageFlavor $vsVersion]
+ set extraSdkPath ""
+ set extraFileListAttributes \
+ [getExtraFileListXmlChunk $packageFlavor $vsVersion]
+} else {
+ fail [appendArgs \
+ "unsupported package flavor, must be one of: " \
+ [list WinRT WinRT81 WP80 WP81 Win32]]
+}
+
###############################################################################
#
@@ -466,7 +636,7 @@ if {![info exists buildNames]} then {
# overridden via the command line or the user-specific customizations
# file.
#
-if {![info exists platformNames]} then {
+if {![info exists platformNames] || [llength $platformNames] == 0} then {
set platformNames [list x86 x64 ARM]
}
diff --git a/tool/omittest.tcl b/tool/omittest.tcl
index 3351b96..5437f2e 100644
--- a/tool/omittest.tcl
+++ b/tool/omittest.tcl
@@ -190,6 +190,7 @@ proc main {argv} {
SQLITE_OMIT_COMPILEOPTION_DIAGS \
SQLITE_OMIT_COMPLETE \
SQLITE_OMIT_COMPOUND_SELECT \
+ SQLITE_OMIT_CTE \
SQLITE_OMIT_DATETIME_FUNCS \
SQLITE_OMIT_DECLTYPE \
SQLITE_OMIT_DEPRECATED \
diff --git a/tool/pagesig.c b/tool/pagesig.c
new file mode 100644
index 0000000..540c9d7
--- /dev/null
+++ b/tool/pagesig.c
@@ -0,0 +1,92 @@
+/*
+** 2013-10-01
+**
+** 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.
+**
+******************************************************************************
+**
+** Compute hash signatures for every page of a database file. This utility
+** program is useful for analyzing the output logs generated by the
+** ext/misc/vfslog.c extension.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+/*
+** Compute signature for a block of content.
+**
+** For blocks of 16 or fewer bytes, the signature is just a hex dump of
+** the entire block.
+**
+** For blocks of more than 16 bytes, the signature is a hex dump of the
+** first 8 bytes followed by a 64-bit has of the entire block.
+*/
+static void vlogSignature(unsigned char *p, int n, char *zCksum){
+ unsigned int s0 = 0, s1 = 0;
+ unsigned int *pI;
+ int i;
+ if( n<=16 ){
+ for(i=0; i<n; i++) sprintf(zCksum+i*2, "%02x", p[i]);
+ }else{
+ pI = (unsigned int*)p;
+ for(i=0; i<n-7; i+=8){
+ s0 += pI[0] + s1;
+ s1 += pI[1] + s0;
+ pI += 2;
+ }
+ for(i=0; i<8; i++) sprintf(zCksum+i*2, "%02x", p[i]);
+ sprintf(zCksum+i*2, "-%08x%08x", s0, s1);
+ }
+}
+
+/*
+** Open a file. Find its page size. Read each page, and compute and
+** display the page signature.
+*/
+static void computeSigs(const char *zFilename){
+ FILE *in = fopen(zFilename, "rb");
+ unsigned pgsz;
+ size_t got;
+ unsigned n;
+ unsigned char aBuf[50];
+ unsigned char aPage[65536];
+
+ if( in==0 ){
+ fprintf(stderr, "cannot open \"%s\"\n", zFilename);
+ return;
+ }
+ got = fread(aBuf, 1, sizeof(aBuf), in);
+ if( got!=sizeof(aBuf) ){
+ goto endComputeSigs;
+ }
+ pgsz = aBuf[16]*256 + aBuf[17];
+ if( pgsz==1 ) pgsz = 65536;
+ if( (pgsz & (pgsz-1))!=0 ){
+ fprintf(stderr, "invalid page size: %02x%02x\n", aBuf[16], aBuf[17]);
+ goto endComputeSigs;
+ }
+ rewind(in);
+ for(n=1; (got=fread(aPage, 1, pgsz, in))==pgsz; n++){
+ vlogSignature(aPage, pgsz, aBuf);
+ printf("%4d: %s\n", n, aBuf);
+ }
+
+endComputeSigs:
+ fclose(in);
+}
+
+/*
+** Find page signatures for all named files.
+*/
+int main(int argc, char **argv){
+ int i;
+ for(i=1; i<argc; i++) computeSigs(argv[i]);
+ return 0;
+}
diff --git a/tool/showdb.c b/tool/showdb.c
index 27424e0..1a51e9d 100644
--- a/tool/showdb.c
+++ b/tool/showdb.c
@@ -9,6 +9,8 @@
#if !defined(_MSC_VER)
#include <unistd.h>
+#else
+#include <io.h>
#endif
#include <stdlib.h>
@@ -66,7 +68,7 @@ static unsigned char *getContent(int ofst, int nByte){
if( aData==0 ) out_of_memory();
memset(aData, 0, nByte+32);
lseek(db, ofst, SEEK_SET);
- read(db, aData, nByte);
+ if( read(db, aData, nByte)<nByte ) memset(aData, 0, nByte);
return aData;
}
@@ -119,7 +121,7 @@ static unsigned char *print_byte_range(
/*
** Print an entire page of content as hex
*/
-static print_page(int iPg){
+static void print_page(int iPg){
int iStart;
unsigned char *aData;
iStart = (iPg-1)*pagesize;
@@ -129,9 +131,10 @@ static print_page(int iPg){
free(aData);
}
+
/* Print a line of decode output showing a 4-byte integer.
*/
-static print_decode_line(
+static void print_decode_line(
unsigned char *aData, /* Content being decoded */
int ofst, int nByte, /* Start and size of decode */
const char *zMsg /* Message to append */
@@ -140,7 +143,7 @@ static print_decode_line(
int val = aData[ofst];
char zBuf[100];
sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]);
- i = strlen(zBuf);
+ i = (int)strlen(zBuf);
for(j=1; j<4; j++){
if( j>=nByte ){
sprintf(&zBuf[i], " ");
@@ -148,7 +151,7 @@ static print_decode_line(
sprintf(&zBuf[i], " %02x", aData[ofst+j]);
val = val*256 + aData[ofst+j];
}
- i += strlen(&zBuf[i]);
+ i += (int)strlen(&zBuf[i]);
}
sprintf(&zBuf[i], " %9d", val);
printf("%s %s\n", zBuf, zMsg);
@@ -189,14 +192,14 @@ static void print_db_header(void){
/*
** Describe cell content.
*/
-static int describeContent(
+static i64 describeContent(
unsigned char *a, /* Cell content */
- int nLocal, /* Bytes in a[] */
+ i64 nLocal, /* Bytes in a[] */
char *zDesc /* Write description here */
){
- int nDesc = 0;
- int n, i, j;
- i64 x, v;
+ i64 nDesc = 0;
+ int n, j;
+ i64 i, x, v;
const unsigned char *pData;
const unsigned char *pLimit;
char sep = ' ';
@@ -236,15 +239,15 @@ static int describeContent(
}else if( x==9 ){
sprintf(zDesc, "1");
}else if( x>=12 ){
- int size = (x-12)/2;
+ i64 size = (x-12)/2;
if( (x&1)==0 ){
- sprintf(zDesc, "blob(%d)", size);
+ sprintf(zDesc, "blob(%lld)", size);
}else{
- sprintf(zDesc, "txt(%d)", size);
+ sprintf(zDesc, "txt(%lld)", size);
}
pData += size;
}
- j = strlen(zDesc);
+ j = (int)strlen(zDesc);
zDesc += j;
nDesc += j;
}
@@ -255,11 +258,11 @@ static int describeContent(
** Compute the local payload size given the total payload size and
** the page size.
*/
-static int localPayload(i64 nPayload, char cType){
- int maxLocal;
- int minLocal;
- int surplus;
- int nLocal;
+static i64 localPayload(i64 nPayload, char cType){
+ i64 maxLocal;
+ i64 minLocal;
+ i64 surplus;
+ i64 nLocal;
if( cType==13 ){
/* Table leaf */
maxLocal = pagesize-35;
@@ -287,19 +290,19 @@ static int localPayload(i64 nPayload, char cType){
**
** The return value is the local cell size.
*/
-static int describeCell(
+static i64 describeCell(
unsigned char cType, /* Page type */
unsigned char *a, /* Cell content */
int showCellContent, /* Show cell content if true */
char **pzDesc /* Store description here */
){
int i;
- int nDesc = 0;
+ i64 nDesc = 0;
int n = 0;
int leftChild;
i64 nPayload;
i64 rowid;
- int nLocal;
+ i64 nLocal;
static char zDesc[1000];
i = 0;
if( cType<=5 ){
@@ -341,6 +344,180 @@ static int describeCell(
return nLocal+n;
}
+/* Print an offset followed by nByte bytes. Add extra white-space
+** at the end so that subsequent text is aligned.
+*/
+static void printBytes(
+ unsigned char *aData, /* Content being decoded */
+ unsigned char *aStart, /* Start of content to be printed */
+ int nByte /* Number of bytes to print */
+){
+ int j;
+ printf(" %03x: ", (int)(aStart-aData));
+ for(j=0; j<9; j++){
+ if( j>=nByte ){
+ printf(" ");
+ }else{
+ printf("%02x ", aStart[j]);
+ }
+ }
+}
+
+
+/*
+** Write a full decode on stdout for the cell at a[ofst].
+** Assume the page contains a header of size szPgHdr bytes.
+*/
+static void decodeCell(
+ unsigned char *a, /* Page content (without the page-1 header) */
+ unsigned pgno, /* Page number */
+ int iCell, /* Cell index */
+ int szPgHdr, /* Size of the page header. 0 or 100 */
+ int ofst /* Cell begins at a[ofst] */
+){
+ int i, j;
+ int leftChild;
+ i64 k;
+ i64 nPayload;
+ i64 rowid;
+ i64 nHdr;
+ i64 iType;
+ i64 nLocal;
+ unsigned char *x = a + ofst;
+ unsigned char *end;
+ unsigned char cType = a[0];
+ int nCol = 0;
+ int szCol[2000];
+ int ofstCol[2000];
+ int typeCol[2000];
+
+ printf("Cell[%d]:\n", iCell);
+ if( cType<=5 ){
+ leftChild = ((x[0]*256 + x[1])*256 + x[2])*256 + x[3];
+ printBytes(a, x, 4);
+ printf("left child page:: %d\n", leftChild);
+ x += 4;
+ }
+ if( cType!=5 ){
+ i = decodeVarint(x, &nPayload);
+ printBytes(a, x, i);
+ nLocal = localPayload(nPayload, cType);
+ if( nLocal==nPayload ){
+ printf("payload-size: %lld\n", nPayload);
+ }else{
+ printf("payload-size: %lld (%lld local, %lld overflow)\n",
+ nPayload, nLocal, nPayload-nLocal);
+ }
+ x += i;
+ }else{
+ nPayload = nLocal = 0;
+ }
+ end = x + nLocal;
+ if( cType==5 || cType==13 ){
+ i = decodeVarint(x, &rowid);
+ printBytes(a, x, i);
+ printf("rowid: %lld\n", rowid);
+ x += i;
+ }
+ if( nLocal>0 ){
+ i = decodeVarint(x, &nHdr);
+ printBytes(a, x, i);
+ printf("record-header-size: %d\n", (int)nHdr);
+ j = i;
+ nCol = 0;
+ k = nHdr;
+ while( x+j<end && j<nHdr ){
+ const char *zTypeName;
+ int sz = 0;
+ char zNm[30];
+ i = decodeVarint(x+j, &iType);
+ printBytes(a, x+j, i);
+ printf("typecode[%d]: %d - ", nCol, (int)iType);
+ switch( iType ){
+ case 0: zTypeName = "NULL"; sz = 0; break;
+ case 1: zTypeName = "int8"; sz = 1; break;
+ case 2: zTypeName = "int16"; sz = 2; break;
+ case 3: zTypeName = "int24"; sz = 3; break;
+ case 4: zTypeName = "int32"; sz = 4; break;
+ case 5: zTypeName = "int48"; sz = 6; break;
+ case 6: zTypeName = "int64"; sz = 8; break;
+ case 7: zTypeName = "double"; sz = 8; break;
+ case 8: zTypeName = "zero"; sz = 0; break;
+ case 9: zTypeName = "one"; sz = 0; break;
+ case 10:
+ case 11: zTypeName = "error"; sz = 0; break;
+ default: {
+ sz = (int)(iType-12)/2;
+ sprintf(zNm, (iType&1)==0 ? "blob(%d)" : "text(%d)", sz);
+ zTypeName = zNm;
+ break;
+ }
+ }
+ printf("%s\n", zTypeName);
+ szCol[nCol] = sz;
+ ofstCol[nCol] = (int)k;
+ typeCol[nCol] = (int)iType;
+ k += sz;
+ nCol++;
+ j += i;
+ }
+ for(i=0; i<nCol && ofstCol[i]+szCol[i]<=nLocal; i++){
+ int s = ofstCol[i];
+ i64 v;
+ const unsigned char *pData;
+ if( szCol[i]==0 ) continue;
+ printBytes(a, x+s, szCol[i]);
+ printf("data[%d]: ", i);
+ pData = x+s;
+ if( typeCol[i]<=7 ){
+ v = (signed char)pData[0];
+ for(k=1; k<szCol[i]; k++){
+ v = (v<<8) + pData[k];
+ }
+ if( typeCol[i]==7 ){
+ double r;
+ memcpy(&r, &v, sizeof(r));
+ printf("%#g\n", r);
+ }else{
+ printf("%lld\n", v);
+ }
+ }else{
+ int ii, jj;
+ char zConst[32];
+ if( (typeCol[i]&1)==0 ){
+ zConst[0] = 'x';
+ zConst[1] = '\'';
+ for(ii=2, jj=0; jj<szCol[i] && ii<24; jj++, ii+=2){
+ sprintf(zConst+ii, "%02x", pData[jj]);
+ }
+ }else{
+ zConst[0] = '\'';
+ for(ii=1, jj=0; jj<szCol[i] && ii<24; jj++, ii++){
+ zConst[ii] = isprint(pData[jj]) ? pData[jj] : '.';
+ }
+ zConst[ii] = 0;
+ }
+ if( jj<szCol[i] ){
+ memcpy(zConst+ii, "...'", 5);
+ }else{
+ memcpy(zConst+ii, "'", 2);
+ }
+ printf("%s\n", zConst);
+ }
+ j = ofstCol[i] + szCol[i];
+ }
+ }
+ if( j<nLocal ){
+ printBytes(a, x+j, 0);
+ printf("... %lld bytes of content ...\n", nLocal-j);
+ }
+ if( nLocal<nPayload ){
+ printBytes(a, x+nLocal, 4);
+ printf("overflow-page: %d\n", decodeInt32(x+nLocal));
+ }
+}
+
+
/*
** Decode a btree page
*/
@@ -356,6 +533,7 @@ static void decode_btree_page(
int iCellPtr;
int showCellContent = 0;
int showMap = 0;
+ int cellToDecode = -2;
char *zMap = 0;
switch( a[0] ){
case 2: zType = "index interior node"; break;
@@ -367,23 +545,37 @@ static void decode_btree_page(
switch( zArgs[0] ){
case 'c': showCellContent = 1; break;
case 'm': showMap = 1; break;
+ case 'd': {
+ if( !isdigit(zArgs[1]) ){
+ cellToDecode = -1;
+ }else{
+ cellToDecode = 0;
+ while( isdigit(zArgs[1]) ){
+ zArgs++;
+ cellToDecode = cellToDecode*10 + zArgs[0] - '0';
+ }
+ }
+ break;
+ }
}
zArgs++;
}
- printf("Decode of btree page %d:\n", pgno);
+ nCell = a[3]*256 + a[4];
+ iCellPtr = (a[0]==2 || a[0]==5) ? 12 : 8;
+ if( cellToDecode>=nCell ){
+ printf("Page %d has only %d cells\n", pgno, nCell);
+ return;
+ }
+ printf("Header on btree page %d:\n", pgno);
print_decode_line(a, 0, 1, zType);
print_decode_line(a, 1, 2, "Offset to first freeblock");
print_decode_line(a, 3, 2, "Number of cells on this page");
- nCell = a[3]*256 + a[4];
print_decode_line(a, 5, 2, "Offset to cell content area");
print_decode_line(a, 7, 1, "Fragmented byte count");
if( a[0]==2 || a[0]==5 ){
print_decode_line(a, 8, 4, "Right child");
- iCellPtr = 12;
- }else{
- iCellPtr = 8;
}
- if( nCell>0 ){
+ if( cellToDecode==(-2) && nCell>0 ){
printf(" key: lx=left-child n=payload-size r=rowid\n");
}
if( showMap ){
@@ -396,27 +588,32 @@ static void decode_btree_page(
for(i=0; i<nCell; i++){
int cofst = iCellPtr + i*2;
char *zDesc;
- int n;
+ i64 n;
cofst = a[cofst]*256 + a[cofst+1];
n = describeCell(a[0], &a[cofst-hdrSize], showCellContent, &zDesc);
if( showMap ){
char zBuf[30];
- memset(&zMap[cofst], '*', n);
+ memset(&zMap[cofst], '*', (size_t)n);
zMap[cofst] = '[';
zMap[cofst+n-1] = ']';
sprintf(zBuf, "%d", i);
- j = strlen(zBuf);
+ j = (int)strlen(zBuf);
if( j<=n-2 ) memcpy(&zMap[cofst+1], zBuf, j);
}
- printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
+ if( cellToDecode==(-2) ){
+ printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
+ }else if( cellToDecode==(-1) || cellToDecode==i ){
+ decodeCell(a, pgno, i, hdrSize, cofst-hdrSize);
+ }
}
if( showMap ){
+ printf("Page map: (H=header P=cell-index 1=page-1-header .=free-space)\n");
for(i=0; i<pagesize; i+=64){
printf(" %03x: %.64s\n", i, &zMap[i]);
}
free(zMap);
- }
+ }
}
/*
@@ -428,7 +625,7 @@ static void decode_trunk_page(
int detail, /* Show leaf pages if true */
int recursive /* Follow the trunk change if true */
){
- int n, i, k;
+ int n, i;
unsigned char *a;
while( pgno>0 ){
a = getContent((pgno-1)*pagesize, pagesize);
@@ -495,11 +692,10 @@ static void page_usage_cell(
int cellno /* Index of the cell on the page */
){
int i;
- int nDesc = 0;
int n = 0;
i64 nPayload;
i64 rowid;
- int nLocal;
+ i64 nLocal;
i = 0;
if( cType<=5 ){
a += 4;
@@ -677,7 +873,7 @@ static void page_usage_report(const char *zDbName){
if( rc==SQLITE_OK ){
while( sqlite3_step(pStmt)==SQLITE_ROW ){
int pgno = sqlite3_column_int(pStmt, 2);
- page_usage_btree(pgno, 0, 0, sqlite3_column_text(pStmt, 1));
+ page_usage_btree(pgno, 0, 0, (const char*)sqlite3_column_text(pStmt,1));
}
}else{
printf("ERROR: cannot query database: %s\n", sqlite3_errmsg(db));
@@ -700,12 +896,12 @@ static void page_usage_report(const char *zDbName){
** Try to figure out how every page in the database file is being used.
*/
static void ptrmap_coverage_report(const char *zDbName){
- unsigned int pgno;
+ int pgno;
unsigned char *aHdr;
unsigned char *a;
int usable;
int perPage;
- unsigned int i;
+ int i;
/* Avoid the pathological case */
if( mxPage<1 ){
@@ -758,6 +954,7 @@ static void usage(const char *argv0){
" NNNb Decode btree page NNN\n"
" NNNbc Decode btree page NNN and show content\n"
" NNNbm Decode btree page NNN and show a layout map\n"
+ " NNNbdCCC Decode cell CCC on btree page NNN\n"
" NNNt Decode freelist trunk page NNN\n"
" NNNtd Show leaf freelist pages on the decode\n"
" NNNtr Recurisvely decode freelist starting at NNN\n"
@@ -779,7 +976,7 @@ int main(int argc, char **argv){
zPgSz[0] = 0;
zPgSz[1] = 0;
lseek(db, 16, SEEK_SET);
- read(db, zPgSz, 2);
+ if( read(db, zPgSz, 2)<2 ) memset(zPgSz, 0, 2);
pagesize = zPgSz[0]*256 + zPgSz[1]*65536;
if( pagesize==0 ) pagesize = 1024;
printf("Pagesize: %d\n", pagesize);
@@ -835,7 +1032,6 @@ int main(int argc, char **argv){
free(a);
continue;
}else if( zLeft && zLeft[0]=='t' ){
- unsigned char *a;
int detail = 0;
int recursive = 0;
int i;
@@ -861,4 +1057,5 @@ int main(int argc, char **argv){
}
}
close(db);
+ return 0;
}
diff --git a/tool/showjournal.c b/tool/showjournal.c
index 5724f52..19220f5 100644
--- a/tool/showjournal.c
+++ b/tool/showjournal.c
@@ -12,7 +12,6 @@
static int pageSize = 1024;
static int sectorSize = 512;
static FILE *db = 0;
-static int showPageContent = 0;
static int fileSize = 0;
static unsigned cksumNonce = 0;
@@ -26,9 +25,9 @@ static void out_of_memory(void){
** Read N bytes of memory starting at iOfst into space obtained
** from malloc().
*/
-static char *read_content(int N, int iOfst){
+static unsigned char *read_content(int N, int iOfst){
int got;
- char *pBuf = malloc(N);
+ unsigned char *pBuf = malloc(N);
if( pBuf==0 ) out_of_memory();
fseek(db, iOfst, SEEK_SET);
got = fread(pBuf, 1, N, db);
@@ -46,14 +45,14 @@ static char *read_content(int N, int iOfst){
/* Print a line of decode output showing a 4-byte integer.
*/
static unsigned print_decode_line(
- unsigned char *aData, /* Content being decoded */
- int ofst, int nByte, /* Start and size of decode */
- const char *zMsg /* Message to append */
+ const unsigned char *aData, /* Content being decoded */
+ int ofst, int nByte, /* Start and size of decode */
+ const char *zMsg /* Message to append */
){
int i, j;
unsigned val = aData[ofst];
char zBuf[100];
- sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]);
+ sprintf(zBuf, " %05x: %02x", ofst, aData[ofst]);
i = strlen(zBuf);
for(j=1; j<4; j++){
if( j>=nByte ){
@@ -74,7 +73,7 @@ static unsigned print_decode_line(
** in global variables.
*/
static unsigned decode_journal_header(int iOfst){
- char *pHdr = read_content(64, iOfst);
+ unsigned char *pHdr = read_content(64, iOfst);
unsigned nPage;
printf("Header at offset %d:\n", iOfst);
print_decode_line(pHdr, 0, 4, "Header part 1 (3654616569)");
@@ -101,12 +100,11 @@ static void print_page(int iOfst){
char zTitle[50];
aData = read_content(pageSize+8, iOfst);
sprintf(zTitle, "page number for page at offset %d", iOfst);
- print_decode_line(aData, 0, 4, zTitle);
+ print_decode_line(aData-iOfst, iOfst, 4, zTitle);
free(aData);
}
int main(int argc, char **argv){
- int rc;
int nPage, cnt;
int iOfst;
if( argc!=2 ){
@@ -136,4 +134,5 @@ int main(int argc, char **argv){
iOfst = (iOfst/sectorSize + 1)*sectorSize;
}
fclose(db);
+ return 0;
}
diff --git a/tool/showstat4.c b/tool/showstat4.c
new file mode 100644
index 0000000..668d210
--- /dev/null
+++ b/tool/showstat4.c
@@ -0,0 +1,157 @@
+/*
+** This utility program decodes and displays the content of the
+** sqlite_stat4 table in the database file named on the command
+** line.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "sqlite3.h"
+
+typedef sqlite3_int64 i64; /* 64-bit signed integer type */
+
+
+/*
+** Convert the var-int format into i64. Return the number of bytes
+** in the var-int. Write the var-int value into *pVal.
+*/
+static int decodeVarint(const unsigned char *z, i64 *pVal){
+ i64 v = 0;
+ int i;
+ for(i=0; i<8; i++){
+ v = (v<<7) + (z[i]&0x7f);
+ if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
+ }
+ v = (v<<8) + (z[i]&0xff);
+ *pVal = v;
+ return 9;
+}
+
+
+
+int main(int argc, char **argv){
+ sqlite3 *db;
+ sqlite3_stmt *pStmt;
+ char *zIdx = 0;
+ int rc, j, x, y, mxHdr;
+ const unsigned char *aSample;
+ int nSample;
+ i64 iVal;
+ const char *zSep;
+
+ if( argc!=2 ){
+ fprintf(stderr, "Usage: %s DATABASE-FILE\n", argv[0]);
+ exit(1);
+ }
+ rc = sqlite3_open(argv[1], &db);
+ if( rc!=SQLITE_OK || db==0 ){
+ fprintf(stderr, "Cannot open database file [%s]\n", argv[1]);
+ exit(1);
+ }
+ rc = sqlite3_prepare_v2(db,
+ "SELECT tbl||'.'||idx, nEq, nLT, nDLt, sample "
+ "FROM sqlite_stat4 ORDER BY 1", -1,
+ &pStmt, 0);
+ if( rc!=SQLITE_OK || pStmt==0 ){
+ fprintf(stderr, "%s\n", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ exit(1);
+ }
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ if( zIdx==0 || strcmp(zIdx, (const char*)sqlite3_column_text(pStmt,0))!=0 ){
+ if( zIdx ) printf("\n");
+ sqlite3_free(zIdx);
+ zIdx = sqlite3_mprintf("%s", sqlite3_column_text(pStmt,0));
+ printf("%s:\n", zIdx);
+ }else{
+ printf(" -----------------------------------------------------------\n");
+ }
+ printf(" nEq = %s\n", sqlite3_column_text(pStmt,1));
+ printf(" nLt = %s\n", sqlite3_column_text(pStmt,2));
+ printf(" nDLt = %s\n", sqlite3_column_text(pStmt,3));
+ printf(" sample = x'");
+ aSample = sqlite3_column_blob(pStmt,4);
+ nSample = sqlite3_column_bytes(pStmt,4);
+ for(j=0; j<nSample; j++) printf("%02x", aSample[j]);
+ printf("'\n ");
+ zSep = " ";
+ x = decodeVarint(aSample, &iVal);
+ if( iVal<x || iVal>nSample ){
+ printf(" <error>\n");
+ continue;
+ }
+ y = mxHdr = (int)iVal;
+ while( x<mxHdr ){
+ int sz;
+ i64 v;
+ x += decodeVarint(aSample+x, &iVal);
+ if( x>mxHdr ) break;
+ if( iVal<0 ) break;
+ switch( iVal ){
+ case 0: sz = 0; break;
+ case 1: sz = 1; break;
+ case 2: sz = 2; break;
+ case 3: sz = 3; break;
+ case 4: sz = 4; break;
+ case 5: sz = 6; break;
+ case 6: sz = 8; break;
+ case 7: sz = 8; break;
+ case 8: sz = 0; break;
+ case 9: sz = 0; break;
+ case 10:
+ case 11: sz = 0; break;
+ default: sz = (int)(iVal-12)/2; break;
+ }
+ if( y+sz>nSample ) break;
+ if( iVal==0 ){
+ printf("%sNULL", zSep);
+ }else if( iVal==8 || iVal==9 ){
+ printf("%s%d", zSep, ((int)iVal)-8);
+ }else if( iVal<=7 ){
+ v = (signed char)aSample[y];
+ for(j=1; j<sz; j++){
+ v = (v<<8) + aSample[y+j];
+ }
+ if( iVal==7 ){
+ double r;
+ memcpy(&r, &v, sizeof(r));
+ printf("%s%#g", zSep, r);
+ }else{
+ printf("%s%lld", zSep, v);
+ }
+ }else if( (iVal&1)==0 ){
+ printf("%sx'", zSep);
+ for(j=0; j<sz; j++){
+ printf("%02x", aSample[y+j]);
+ }
+ printf("'");
+ }else{
+ printf("%s\"", zSep);
+ for(j=0; j<sz; j++){
+ char c = (char)aSample[y+j];
+ if( isprint(c) ){
+ if( c=='"' || c=='\\' ) putchar('\\');
+ putchar(c);
+ }else if( c=='\n' ){
+ printf("\\n");
+ }else if( c=='\t' ){
+ printf("\\t");
+ }else if( c=='\r' ){
+ printf("\\r");
+ }else{
+ printf("\\%03o", c);
+ }
+ }
+ printf("\"");
+ }
+ zSep = ",";
+ y += sz;
+ }
+ printf("\n");
+ }
+ sqlite3_free(zIdx);
+ sqlite3_finalize(pStmt);
+ sqlite3_close(db);
+ return 0;
+}
diff --git a/tool/showwal.c b/tool/showwal.c
index 2888c10..6dc1de1 100644
--- a/tool/showwal.c
+++ b/tool/showwal.c
@@ -6,7 +6,13 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+
+#if !defined(_MSC_VER)
#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
#include <stdlib.h>
#include <string.h>
@@ -172,7 +178,7 @@ static void print_decode_line(
int val = aData[ofst];
char zBuf[100];
sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]);
- i = strlen(zBuf);
+ i = (int)strlen(zBuf);
for(j=1; j<4; j++){
if( j>=nByte ){
sprintf(&zBuf[i], " ");
@@ -180,7 +186,7 @@ static void print_decode_line(
sprintf(&zBuf[i], " %02x", aData[ofst+j]);
val = val*256 + aData[ofst+j];
}
- i += strlen(&zBuf[i]);
+ i += (int)strlen(&zBuf[i]);
}
if( asHex ){
sprintf(&zBuf[i], " 0x%08x", val);
@@ -273,14 +279,14 @@ static void print_wal_header(Cksum *pCksum){
/*
** Describe cell content.
*/
-static int describeContent(
+static i64 describeContent(
unsigned char *a, /* Cell content */
- int nLocal, /* Bytes in a[] */
+ i64 nLocal, /* Bytes in a[] */
char *zDesc /* Write description here */
){
int nDesc = 0;
- int n, i, j;
- i64 x, v;
+ int n, j;
+ i64 i, x, v;
const unsigned char *pData;
const unsigned char *pLimit;
char sep = ' ';
@@ -320,15 +326,15 @@ static int describeContent(
}else if( x==9 ){
sprintf(zDesc, "1");
}else if( x>=12 ){
- int size = (x-12)/2;
+ i64 size = (x-12)/2;
if( (x&1)==0 ){
- sprintf(zDesc, "blob(%d)", size);
+ sprintf(zDesc, "blob(%lld)", size);
}else{
- sprintf(zDesc, "txt(%d)", size);
+ sprintf(zDesc, "txt(%lld)", size);
}
pData += size;
}
- j = strlen(zDesc);
+ j = (int)strlen(zDesc);
zDesc += j;
nDesc += j;
}
@@ -339,11 +345,11 @@ static int describeContent(
** Compute the local payload size given the total payload size and
** the page size.
*/
-static int localPayload(i64 nPayload, char cType){
- int maxLocal;
- int minLocal;
- int surplus;
- int nLocal;
+static i64 localPayload(i64 nPayload, char cType){
+ i64 maxLocal;
+ i64 minLocal;
+ i64 surplus;
+ i64 nLocal;
if( cType==13 ){
/* Table leaf */
maxLocal = pagesize-35;
@@ -370,19 +376,19 @@ static int localPayload(i64 nPayload, char cType){
**
** The return value is the local cell size.
*/
-static int describeCell(
+static i64 describeCell(
unsigned char cType, /* Page type */
unsigned char *a, /* Cell content */
int showCellContent, /* Show cell content if true */
char **pzDesc /* Store description here */
){
int i;
- int nDesc = 0;
+ i64 nDesc = 0;
int n = 0;
int leftChild;
i64 nPayload;
i64 rowid;
- int nLocal;
+ i64 nLocal;
static char zDesc[1000];
i = 0;
if( cType<=5 ){
@@ -479,17 +485,17 @@ static void decode_btree_page(
for(i=0; i<nCell; i++){
int cofst = iCellPtr + i*2;
char *zDesc;
- int n;
+ i64 n;
cofst = a[cofst]*256 + a[cofst+1];
n = describeCell(a[0], &a[cofst-hdrSize], showCellContent, &zDesc);
if( showMap ){
char zBuf[30];
- memset(&zMap[cofst], '*', n);
+ memset(&zMap[cofst], '*', (size_t)n);
zMap[cofst] = '[';
zMap[cofst+n-1] = ']';
sprintf(zBuf, "%d", i);
- j = strlen(zBuf);
+ j = (int)strlen(zBuf);
if( j<=n-2 ) memcpy(&zMap[cofst+1], zBuf, j);
}
printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
diff --git a/tool/spaceanal.tcl b/tool/spaceanal.tcl
index 6988f6e..a227b85 100644
--- a/tool/spaceanal.tcl
+++ b/tool/spaceanal.tcl
@@ -199,15 +199,17 @@ foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
# is.
#
set gap_cnt 0
- set pglist [db eval {
- SELECT pageno FROM temp.dbstat WHERE name = $name ORDER BY rowid
- }]
- set prev [lindex $pglist 0]
- foreach pgno [lrange $pglist 1 end] {
- if {$pgno != $prev+1} {incr gap_cnt}
- set prev $pgno
+ set prev 0
+ db eval {
+ SELECT pageno, pagetype FROM temp.dbstat
+ WHERE name=$name
+ ORDER BY pageno
+ } {
+ if {$prev>0 && $pagetype=="leaf" && $pageno!=$prev+1} {
+ incr gap_cnt
+ }
+ set prev $pageno
}
-
mem eval {
INSERT INTO space_used VALUES(
$name,
@@ -246,8 +248,19 @@ mem function int integerify
# [quote {hello world's}] == {'hello world''s'}
#
proc quote {txt} {
- regsub -all ' $txt '' q
- return '$q'
+ return [string map {' ''} $txt]
+}
+
+# Output a title line
+#
+proc titleline {title} {
+ if {$title==""} {
+ puts [string repeat * 79]
+ } else {
+ set len [string length $title]
+ set stars [string repeat * [expr 79-$len-5]]
+ puts "*** $title $stars"
+ }
}
# Generate a single line of output in the statistics section of the
@@ -255,7 +268,7 @@ proc quote {txt} {
#
proc statline {title value {extra {}}} {
set len [string length $title]
- set dots [string range {......................................} $len end]
+ set dots [string repeat . [expr 50-$len]]
set len [string length $value]
set sp2 [string range { } $len end]
if {$extra ne ""} {
@@ -287,7 +300,7 @@ proc divide {num denom} {
# Generate a subreport that covers some subset of the database.
# the $where clause determines which subset to analyze.
#
-proc subreport {title where} {
+proc subreport {title where showFrag} {
global pageSize file_pgcnt compressOverhead
# Query the in-memory database for the sum of various statistics
@@ -319,9 +332,7 @@ proc subreport {title where} {
# Output the sub-report title, nicely decorated with * characters.
#
puts ""
- set len [string length $title]
- set stars [string repeat * [expr 65-$len]]
- puts "*** $title $stars"
+ titleline $title
puts ""
# Calculate statistics and store the results in TCL variables, as follows:
@@ -375,9 +386,9 @@ proc subreport {title where} {
if {[info exists avg_fanout]} {
statline {Average fanout} $avg_fanout
}
- if {$total_pages>1} {
- set fragmentation [percent $gap_cnt [expr {$total_pages-1}] {fragmentation}]
- statline {Fragmentation} $fragmentation
+ if {$showFrag && $total_pages>1} {
+ set fragmentation [percent $gap_cnt [expr {$total_pages-1}]]
+ statline {Non-sequential pages} $gap_cnt $fragmentation
}
statline {Maximum payload per entry} $mx_payload
statline {Entries that use overflow} $ovfl_cnt $ovfl_cnt_percent
@@ -490,9 +501,6 @@ set user_percent [percent $user_payload $file_bytes]
# Output the summary statistics calculated above.
#
puts "/** Disk-Space Utilization Report For $root_filename"
-catch {
- puts "*** As of [clock format [clock seconds] -format {%Y-%b-%d %H:%M:%S}]"
-}
puts ""
statline {Page size in bytes} $pageSize
statline {Pages in the whole file (measured)} $file_pgcnt
@@ -503,8 +511,8 @@ statline {Pages on the freelist (calculated)} $free_pgcnt $free_percent
statline {Pages of auto-vacuum overhead} $av_pgcnt $av_percent
statline {Number of tables in the database} $ntable
statline {Number of indices} $nindex
-statline {Number of named indices} $nmanindex
-statline {Automatically generated indices} $nautoindex
+statline {Number of defined indices} $nmanindex
+statline {Number of implied indices} $nautoindex
if {$isCompressed} {
statline {Size of uncompressed content in bytes} $file_bytes
set efficiency [percent $true_file_size $file_bytes]
@@ -517,16 +525,27 @@ statline {Bytes of user payload stored} $user_payload $user_percent
# Output table rankings
#
puts ""
-puts "*** Page counts for all tables with their indices ********************"
+titleline "Page counts for all tables with their indices"
puts ""
mem eval {SELECT tblname, count(*) AS cnt,
int(sum(int_pages+leaf_pages+ovfl_pages)) AS size
FROM space_used GROUP BY tblname ORDER BY size+0 DESC, tblname} {} {
statline [string toupper $tblname] $size [percent $size $file_pgcnt]
}
+puts ""
+titleline "Page counts for all tables and indices separately"
+puts ""
+mem eval {
+ SELECT
+ upper(name) AS nm,
+ int(int_pages+leaf_pages+ovfl_pages) AS size
+ FROM space_used
+ ORDER BY size+0 DESC, name} {} {
+ statline $nm $size [percent $size $file_pgcnt]
+}
if {$isCompressed} {
puts ""
- puts "*** Bytes of disk space used after compression ***********************"
+ titleline "Bytes of disk space used after compression"
puts ""
set csum 0
mem eval {SELECT tblname,
@@ -546,31 +565,40 @@ if {$isCompressed} {
# Output subreports
#
if {$nindex>0} {
- subreport {All tables and indices} 1
+ subreport {All tables and indices} 1 0
}
-subreport {All tables} {NOT is_index}
+subreport {All tables} {NOT is_index} 0
if {$nindex>0} {
- subreport {All indices} {is_index}
+ subreport {All indices} {is_index} 0
}
-foreach tbl [mem eval {SELECT name FROM space_used WHERE NOT is_index
+foreach tbl [mem eval {SELECT DISTINCT tblname name FROM space_used
ORDER BY name}] {
- regsub ' $tbl '' qn
+ set qn [quote $tbl]
set name [string toupper $tbl]
- set n [mem eval "SELECT count(*) FROM space_used WHERE tblname='$qn'"]
+ set n [mem eval {SELECT count(*) FROM space_used WHERE tblname=$tbl}]
if {$n>1} {
- subreport "Table $name and all its indices" "tblname='$qn'"
- subreport "Table $name w/o any indices" "name='$qn'"
- subreport "Indices of table $name" "tblname='$qn' AND is_index"
+ set idxlist [mem eval "SELECT name FROM space_used
+ WHERE tblname='$qn' AND is_index
+ ORDER BY 1"]
+ subreport "Table $name and all its indices" "tblname='$qn'" 0
+ subreport "Table $name w/o any indices" "name='$qn'" 1
+ if {[llength $idxlist]>1} {
+ subreport "Indices of table $name" "tblname='$qn' AND is_index" 0
+ }
+ foreach idx $idxlist {
+ set qidx [quote $idx]
+ subreport "Index [string toupper $idx] of table $name" "name='$qidx'" 1
+ }
} else {
- subreport "Table $name" "name='$qn'"
+ subreport "Table $name" "name='$qn'" 1
}
}
# Output instructions on what the numbers above mean.
#
+puts ""
+titleline Definitions
puts {
-*** Definitions ******************************************************
-
Page size in bytes
The number of bytes in a single page of the database file.
@@ -607,11 +635,11 @@ Number of indices
The total number of indices in the database.
-Number of named indices
+Number of defined indices
The number of indices created using an explicit CREATE INDEX statement.
-Automatically generated indices
+Number of implied indices
The number of indices used to implement PRIMARY KEY or UNIQUE constraints
on tables.
@@ -660,13 +688,16 @@ Average unused bytes per entry
category on a per-entry basis. This is the number of unused bytes on
all pages divided by the number of entries.
-Fragmentation
+Non-sequential pages
- The percentage of pages in the table or index that are not
- consecutive in the disk file. Many filesystems are optimized
- for sequential file access so smaller fragmentation numbers
- sometimes result in faster queries, especially for larger
- database files that do not fit in the disk cache.
+ The number of pages in the table or index that are out of sequence.
+ Many filesystems are optimized for sequential file access so a small
+ number of non-sequential pages might result in faster queries,
+ especially for larger database files that do not fit in the disk cache.
+ Note that after running VACUUM, the root page of each table or index is
+ at the beginning of the database file and all other pages are in a
+ separate part of the database file, resulting in a single non-
+ sequential page.
Maximum payload per entry
@@ -722,7 +753,7 @@ Unused bytes on all pages
# Output a dump of the in-memory database. This can be used for more
# complex offline analysis.
#
-puts "**********************************************************************"
+titleline {}
puts "The entire text of this report can be sourced into any SQL database"
puts "engine for further analysis. All of the text above is an SQL comment."
puts "The data used to generate this report follows:"
@@ -735,7 +766,7 @@ mem eval {SELECT * FROM space_used} x {
set sep (
foreach col $x(*) {
set v $x($col)
- if {$v=="" || ![string is double $v]} {set v [quote $v]}
+ if {$v=="" || ![string is double $v]} {set v '[quote $v]'}
puts -nonewline $sep$v
set sep ,
}
diff --git a/tool/vdbe-compress.tcl b/tool/vdbe-compress.tcl
index 95cc1eb..a349830 100644
--- a/tool/vdbe-compress.tcl
+++ b/tool/vdbe-compress.tcl
@@ -13,7 +13,7 @@
# Script usage:
#
# mv vdbe.c vdbe.c.template
-# tclsh vdbe-compress.tcl <vdbe.c.template >vdbe.c
+# tclsh vdbe-compress.tcl $CFLAGS <vdbe.c.template >vdbe.c
#
# Modifications made:
#
@@ -42,6 +42,16 @@ set unionDef {} ;# C code of the union
set afterUnion {} ;# C code after the union
set sCtr 0 ;# Context counter
+# If the SQLITE_SMALL_STACK compile-time option is missing, then
+# this transformation becomes a no-op.
+#
+if {![regexp {SQLITE_SMALL_STACK} $argv]} {
+ while {![eof stdin]} {
+ puts [gets stdin]
+ }
+ exit
+}
+
# Read program text up to the spot where the union should be
# inserted.
#
diff --git a/tool/vdbe_profile.tcl b/tool/vdbe_profile.tcl
new file mode 100644
index 0000000..fb1f955
--- /dev/null
+++ b/tool/vdbe_profile.tcl
@@ -0,0 +1,82 @@
+#!/bin/tclsh
+#
+# Run this script in the same directory as the "vdbe_profile.out" file.
+# This script summarizes the results contained in that file.
+#
+if {![file readable vdbe_profile.out]} {
+ error "run this script in the same directory as the vdbe_profile.out file"
+}
+set in [open vdbe_profile.out r]
+set stmt {}
+set allstmt {}
+while {![eof $in]} {
+ set line [gets $in]
+ if {$line==""} continue
+ if {[regexp {^---- } $line]} {
+ set stmt [lindex $line 1]
+ if {[info exists cnt($stmt)]} {
+ incr cnt($stmt)
+ set firsttime 0
+ } else {
+ set cnt($stmt) 1
+ set sql($stmt) {}
+ set firsttime 1
+ lappend allstmt $stmt
+ }
+ continue;
+ }
+ if {[regexp {^-- } $line]} {
+ if {$firsttime} {
+ append sql($stmt) [string range $line 3 end]\n
+ }
+ continue
+ }
+ if {![regexp {^ *\d+ *\d+ *\d+ *\d+ ([A-Z].*)} $line all detail]} continue
+ set c [lindex $line 0]
+ set t [lindex $line 1]
+ set addr [lindex $line 3]
+ set op [lindex $line 4]
+ if {[info exists opcnt($op)]} {
+ incr opcnt($op) $c
+ incr opcycle($op) $t
+ } else {
+ set opcnt($op) $c
+ set opcycle($op) $t
+ }
+ if {[info exists stat($stmt,$addr)]} {
+ foreach {cx tx detail} $stat($stmt,$addr) break
+ incr cx $c
+ incr tx $t
+ set stat($stmt,$addr) [list $cx $tx $detail]
+ } else {
+ set stat($stmt,$addr) [list $c $t $detail]
+ }
+}
+close $in
+
+foreach stmt $allstmt {
+ puts "********************************************************************"
+ puts [string trim $sql($stmt)]
+ puts "Execution count: $cnt($stmt)"
+ for {set i 0} {[info exists stat($stmt,$i)]} {incr i} {
+ foreach {cx tx detail} $stat($stmt,$i) break
+ if {$cx==0} {
+ set ax 0
+ } else {
+ set ax [expr {$tx/$cx}]
+ }
+ puts [format {%8d %12d %12d %4d %s} $cx $tx $ax $i $detail]
+ }
+}
+puts "********************************************************************"
+puts "OPCODES:"
+foreach op [lsort [array names opcnt]] {
+ set cx $opcnt($op)
+ set tx $opcycle($op)
+ if {$cx==0} {
+ set ax 0
+ } else {
+ set ax [expr {$tx/$cx}]
+ }
+ puts [format {%8d %12d %12d %s} $cx $tx $ax $op]
+}
diff --git a/tool/warnings.sh b/tool/warnings.sh
index 78cfb55..5d71361 100644
--- a/tool/warnings.sh
+++ b/tool/warnings.sh
@@ -4,14 +4,14 @@
# compiler warnings in SQLite.
#
rm -f sqlite3.c
-make sqlite3.c-debug
+make sqlite3.c
echo '********** No optimizations. Includes FTS4 and RTREE *********'
gcc -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \
-ansi -DHAVE_STDINT_H -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE \
sqlite3.c
-echo '********** No optimizations. ENABLE_STAT3. THREADSAFE=0 *******'
+echo '********** No optimizations. ENABLE_STAT4. THREADSAFE=0 *******'
gcc -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \
- -ansi -DSQLITE_ENABLE_STAT3 -DSQLITE_THREADSAFE=0 \
+ -ansi -DSQLITE_ENABLE_STAT4 -DSQLITE_THREADSAFE=0 \
sqlite3.c
echo '********** Optimized -O3. Includes FTS4 and RTREE ************'
gcc -O3 -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \
diff --git a/tool/win/sqlite.vsix b/tool/win/sqlite.vsix
index 93eefac..1450011 100644
--- a/tool/win/sqlite.vsix
+++ b/tool/win/sqlite.vsix
Binary files differ