Index: tool/dbhash.c ================================================================== --- tool/dbhash.c +++ tool/dbhash.c @@ -8,11 +8,11 @@ ** 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 computes a hash on the content +** This is a utility program that computes an SHA1 hash on the content ** of an SQLite database. ** ** The hash is computed over just the content of the database. Free ** space inside of the database file, and alternative on-disk representations ** of the same content (ex: UTF8 vs UTF16) do not affect the hash. So, @@ -38,16 +38,20 @@ /* ** All global variables are gathered into the "g" singleton. */ struct GlobalVars { const char *zArgv0; /* Name of program */ - int bSchemaPK; /* Use the schema-defined PK, not the true PK */ unsigned fDebug; /* Debug flags */ sqlite3 *db; /* The database connection */ SHA1Context cx; /* SHA1 hash context */ } g; +/* +** Debugging flags +*/ +#define DEBUG_FULLTRACE 0x00000001 /* Trace hash to stderr */ + /****************************************************************************** ** The Hash Engine ** ** Modify these routines (and appropriate state fields in global variable 'g') ** in order to compute a different (better?) hash of the database. @@ -198,11 +202,11 @@ (void)memcpy(&g.cx.buffer[j], &data[i], len - i); } /* Add padding and compute and output the message digest. */ -static void hash_finish(void){ +static void hash_finish(const char *zName){ unsigned int i; unsigned char finalcount[8]; unsigned char digest[20]; static const char zEncode[] = "0123456789abcdef"; char zOut[40]; @@ -222,11 +226,11 @@ for(i=0; i<20; i++){ zOut[i*2] = zEncode[(digest[i]>>4)&0xf]; zOut[i*2+1] = zEncode[digest[i] & 0xf]; } zOut[i*2]= 0; - printf("%s\n", zOut); + printf("%s %s\n", zOut, zName); } /* End of the hashing logic *******************************************************************************/ /* @@ -284,23 +288,32 @@ va_end(ap); return pStmt; } /* -** Compute the hash for a single table named zTab +** Compute the hash for all rows of the query formed from the printf-style +** zFormat and its argument. */ -static void hash_one_table(const char *zTab){ - sqlite3_stmt *pStmt; - int nCol; - int i; - pStmt = db_prepare("SELECT * FROM \"%w\";", zTab); +static void hash_one_query(const char *zFormat, ...){ + va_list ap; + sqlite3_stmt *pStmt; /* The query defined by zFormat and "..." */ + int nCol; /* Number of columns in the result set */ + int i; /* Loop counter */ + + /* Prepare the query defined by zFormat and "..." */ + va_start(ap, zFormat); + pStmt = db_vprepare(zFormat, ap); + va_end(ap); nCol = sqlite3_column_count(pStmt); + + /* Compute a hash over the result of the query */ while( SQLITE_ROW==sqlite3_step(pStmt) ){ for(i=0; i>= 8; } hash_step((const unsigned char*)"1",1); hash_step(x,8); + if( g.fDebug & DEBUG_FULLTRACE ){ + fprintf(stderr, "INT %s\n", sqlite3_column_text(pStmt,i)); + } break; } case SQLITE_FLOAT: { sqlite3_uint64 u; int j; @@ -325,24 +341,33 @@ x[j] = u & 0xff; u >>= 8; } hash_step((const unsigned char*)"2",1); hash_step(x,8); + if( g.fDebug & DEBUG_FULLTRACE ){ + fprintf(stderr, "FLOAT %s\n", sqlite3_column_text(pStmt,i)); + } break; } case SQLITE_TEXT: { int n = sqlite3_column_bytes(pStmt, i); const unsigned char *z = sqlite3_column_text(pStmt, i); hash_step((const unsigned char*)"3", 1); hash_step(z, n); + if( g.fDebug & DEBUG_FULLTRACE ){ + fprintf(stderr, "TEXT '%s'\n", sqlite3_column_text(pStmt,i)); + } break; } case SQLITE_BLOB: { int n = sqlite3_column_bytes(pStmt, i); const unsigned char *z = sqlite3_column_blob(pStmt, i); hash_step((const unsigned char*)"4", 1); hash_step(z, n); + if( g.fDebug & DEBUG_FULLTRACE ){ + fprintf(stderr, "BLOB (%d bytes)\n", n); + } break; } } } } @@ -352,22 +377,32 @@ /* ** Print sketchy documentation for this utility program */ static void showHelp(void){ - printf("Usage: %s DB\n", g.zArgv0); + printf("Usage: %s [options] FILE ...\n", g.zArgv0); printf( -"Compute a hash on the content of database DB\n" +"Compute a SHA1 hash on the content of database FILE. System tables such as\n" +"sqlite_stat1, sqlite_stat4, and sqlite_sequence are omitted from the hash.\n" +"Options:\n" +" --debug N Set debugging flags to N (experts only)\n" +" --like PATTERN Only hash tables whose name is LIKE the pattern\n" +" --schema-only Only hash the schema - omit table content\n" +" --without-schema Only hash table content - omit the schema\n" ); } int main(int argc, char **argv){ - const char *zDb = 0; - int i; - int rc; - char *zErrMsg; - sqlite3_stmt *pStmt; + const char *zDb = 0; /* Name of the database currently being hashed */ + int i; /* Loop counter */ + int rc; /* Subroutine return code */ + char *zErrMsg; /* Error message when opening database */ + sqlite3_stmt *pStmt; /* An SQLite query */ + const char *zLike = 0; /* LIKE pattern of tables to hash */ + int omitSchema = 0; /* True to compute hash on content only */ + int omitContent = 0; /* True to compute hash on schema only */ + int nFile = 0; /* Number of input filenames seen */ g.zArgv0 = argv[0]; sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); for(i=1; i