Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -494,10 +494,14 @@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB) dbhash$(EXE): $(TOP)/tool/dbhash.c sqlite3.c sqlite3.h $(TCCX) -o dbhash$(EXE) -DSQLITE_THREADSAFE=0 \ $(TOP)/tool/dbhash.c sqlite3.c $(TLIBS) $(THREADLIB) + +faststat1$(EXE): $(TOP)/tool/faststat1.c sqlite3.c sqlite3.h + $(TCCX) -o faststat1$(EXE) -DSQLITE_THREADSAFE=0 \ + $(TOP)/tool/faststat1.c sqlite3.c $(TLIBS) $(THREADLIB) scrub$(EXE): $(TOP)/ext/misc/scrub.c sqlite3.o $(TCC) -I. -DSCRUB_STANDALONE -o scrub$(EXE) $(TOP)/ext/misc/scrub.c sqlite3.o $(THREADLIB) srcck1$(EXE): $(TOP)/tool/srcck1.c ADDED tool/faststat1.c Index: tool/faststat1.c ================================================================== --- /dev/null +++ tool/faststat1.c @@ -0,0 +1,423 @@ +/* +** 2016-10-24 +** +** 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 is a utility program that uses the est_count and btree_sample +** pragmas to try to approximate the content of the sqlite_stat1 table +** without doing a full table scan. +** +** To compile, simply link against SQLite. +** +** See the showHelp() routine below for a brief description of how to +** run the utility. +*/ +#include +#include +#include +#include +#include +#include +#include "sqlite3.h" + +/* +** All global variables are gathered into the "g" singleton. +*/ +struct GlobalVars { + const char *zArgv0; /* Name of program */ + unsigned fDebug; /* Debug flags */ + sqlite3 *db; /* The database connection */ +} g; + +/* +** Allowed values for g.fDebug +*/ +#define DEBUG_NONE 0 + + +/* +** Print an error resulting from faulting command-line arguments and +** abort the program. +*/ +static void cmdlineError(const char *zFormat, ...){ + va_list ap; + fprintf(stderr, "%s: ", g.zArgv0); + va_start(ap, zFormat); + vfprintf(stderr, zFormat, ap); + va_end(ap); + fprintf(stderr, "\n\"%s --help\" for more help\n", g.zArgv0); + exit(1); +} + +/* +** Print an error message for an error that occurs at runtime, then +** abort the program. +*/ +static void runtimeError(const char *zFormat, ...){ + va_list ap; + fprintf(stderr, "%s: ", g.zArgv0); + va_start(ap, zFormat); + vfprintf(stderr, zFormat, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(1); +} + +/* +** Prepare a new SQL statement. Print an error and abort if anything +** goes wrong. +*/ +static sqlite3_stmt *db_vprepare(const char *zFormat, va_list ap){ + char *zSql; + int rc; + sqlite3_stmt *pStmt; + + zSql = sqlite3_vmprintf(zFormat, ap); + if( zSql==0 ) runtimeError("out of memory"); + rc = sqlite3_prepare_v2(g.db, zSql, -1, &pStmt, 0); + if( rc ){ + runtimeError("SQL statement error: %s\n\"%s\"", sqlite3_errmsg(g.db), + zSql); + } + sqlite3_free(zSql); + return pStmt; +} +static sqlite3_stmt *db_prepare(const char *zFormat, ...){ + va_list ap; + sqlite3_stmt *pStmt; + va_start(ap, zFormat); + pStmt = db_vprepare(zFormat, ap); + va_end(ap); + return pStmt; +} + +/* +** Estimate the number of rows in the given table or index. +*/ +static sqlite3_int64 estEntryCount(const char *zTabIdx){ + double sum = 0.0; + int i; + int n = 0; + sqlite3_stmt *pStmt; +# define N_CNT_SAMPLE 10 + for(i=0; i<=N_CNT_SAMPLE; i++){ + pStmt = db_prepare("PRAGMA est_count(\"%w\",%g)", + zTabIdx, ((double)i)/(double)(N_CNT_SAMPLE)); + if( sqlite3_step(pStmt)==SQLITE_ROW ){ + sum += sqlite3_column_double(pStmt, 0); + n++; + } + sqlite3_finalize(pStmt); + } + return n==0 ? 0 : (sqlite3_int64)(sum/n); +} + +/* +** Stat1 for a table. +*/ +static void analyzeTable(const char *zTab){ + sqlite3_int64 n = estEntryCount(zTab); + sqlite3_stmt *pStmt; + if( n==0 ){ + printf("-- empty table: %s\n", zTab); + return; + } + pStmt = db_prepare( + "INSERT INTO temp.est_stat1(tbl,idx,stat)" + "VALUES(\"%w\",NULL,'%lld')", zTab, n + ); + sqlite3_step(pStmt); + sqlite3_finalize(pStmt); +} + +/* +** Compare the i-th column of pStmt against pValue. Return true if they +** are different. +*/ +static int columnNotEqual(sqlite3_stmt *pStmt, int i, sqlite3_value *pValue){ + int n1, n2, n; + if( sqlite3_column_type(pStmt,i)!=sqlite3_value_type(pValue) ) return 1; + switch( sqlite3_column_type(pStmt,i) ){ + case SQLITE_NULL: + return 0; /* Nulls compare equal to one another in this context */ + + case SQLITE_INTEGER: + return sqlite3_column_int64(pStmt,i)!=sqlite3_value_int64(pValue); + + case SQLITE_FLOAT: + return sqlite3_column_double(pStmt,i)!=sqlite3_value_double(pValue); + + case SQLITE_BLOB: + n1 = sqlite3_column_bytes(pStmt,i); + n2 = sqlite3_value_bytes(pValue); + n = n110000 ? 100 : 20000; + pStmt = db_prepare("PRAGMA btree_sample(\"%w\",0.0,%lld)", + zIdx, n*2); + for(i=0; i0" + " AND (type='index' OR name NOT LIKE 'sqlite_%%')" + " ORDER BY tbl_name, type DESC, name"); + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + const char *zType = (const char*)sqlite3_column_text(pStmt, 0); + const char *zName = (const char*)sqlite3_column_text(pStmt, 1); + const char *zTblName = (const char*)sqlite3_column_text(pStmt, 2); + if( zType[0]=='t' ){ + analyzeTable(zName); + }else{ + analyzeIndex(zTblName, zName); + } + } + sqlite3_finalize(pStmt); + dump_table("temp.est_stat1","sqlite_stat1"); + sqlite3_close(g.db); + return 0; +}