]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix the dbhash utility so that it ignores the root page number when hashing
authordrh <drh@noemail.net>
Wed, 8 Jun 2016 13:49:28 +0000 (13:49 +0000)
committerdrh <drh@noemail.net>
Wed, 8 Jun 2016 13:49:28 +0000 (13:49 +0000)
the sqlite_master table.  Add new command-line options.  Add the ability to
hash multiple databases with a single command.

FossilOrigin-Name: 44f157e0f0d5a76ef9002b2592164c4fdae89e34

manifest
manifest.uuid
tool/dbhash.c

index 7d7f2a3501f77e504496d79dcb2a5a6e0a66ce53..efda9daef29934ac44c5c7ad9413dad56491276e 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C An\sinitial\sattempt\sat\sa\s"dbhash"\scommand-line\sutility.
-D 2016-06-08T01:03:05.100
+C Fix\sthe\sdbhash\sutility\sso\sthat\sit\signores\sthe\sroot\spage\snumber\swhen\shashing\nthe\ssqlite_master\stable.\s\sAdd\snew\scommand-line\soptions.\s\sAdd\sthe\sability\sto\nhash\smultiple\sdatabases\swith\sa\ssingle\scommand.
+D 2016-06-08T13:49:28.865
 F Makefile.in f3f7d2060ce03af4584e711ef3a626ef0b1d6340
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 50149765ef72f4e652b9a0f1f6462c4784bb9423
@@ -1420,7 +1420,7 @@ F tool/build-all-msvc.bat 3e4e4043b53f1aede4308e0d2567bbd773614630 x
 F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
 F tool/cg_anno.tcl 692ce4b8693d59e3a3de77ca97f4139ecfa641b0 x
 F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2
-F tool/dbhash.c 11ae59057c1ebbd54ac10bdc59c8fc7e0a43a156
+F tool/dbhash.c 3e7a97ebdaff842f99ee1eab3787717582d665b8
 F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
 F tool/fast_vacuum.c 5ba0d6f5963a0a63bdc42840f678bad75b2ebce1
 F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
@@ -1501,10 +1501,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 2091a4c9231c7871f27661adc27dd7df26500f6c
-R 2cb484710443ce45b289f472a9c90e73
-T *branch * dbhash
-T *sym-dbhash *
-T -sym-trunk *
+P 2247649ca215c06205b33b2250eb809baf39263a
+R 8393151ede059e20b861d67944f07343
 U drh
-Z 1329a111dd8194eb5ec209c8b1ad3288
+Z e6c66d0412cbc063d2ea74dc380f683a
index ca0df0561caee344af7961825121a8567fe68970..8a0b15bbb6201a833193c0b6d1850ffb75078910 100644 (file)
@@ -1 +1 @@
-2247649ca215c06205b33b2250eb809baf39263a
\ No newline at end of file
+44f157e0f0d5a76ef9002b2592164c4fdae89e34
\ No newline at end of file
index 0e1b02b6735eedd4c6324e47b0e20ff4a1449846..8873c39c2a7c6c41fb1b057dde275a30e2de2d3a 100644 (file)
@@ -10,7 +10,7 @@
 **
 *************************************************************************
 **
-** 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
@@ -40,12 +40,16 @@ struct SHA1Context {
 */
 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
 **
@@ -200,7 +204,7 @@ static void hash_step(const unsigned char *data,  unsigned int len){
 
 
 /* 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];
@@ -224,7 +228,7 @@ static void hash_finish(void){
     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
 *******************************************************************************/
@@ -286,19 +290,28 @@ static sqlite3_stmt *db_prepare(const char *zFormat, ...){
 }
 
 /*
-** 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<nCol; i++){
       switch( sqlite3_column_type(pStmt,i) ){
         case SQLITE_NULL: {
           hash_step((const unsigned char*)"0",1);
+          if( g.fDebug & DEBUG_FULLTRACE ) fprintf(stderr, "NULL\n");
           break;
         }
         case SQLITE_INTEGER: {
@@ -313,6 +326,9 @@ static void hash_one_table(const char *zTab){
           }
           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: {
@@ -327,6 +343,9 @@ static void hash_one_table(const char *zTab){
           }
           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: {
@@ -334,6 +353,9 @@ static void hash_one_table(const char *zTab){
           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: {
@@ -341,6 +363,9 @@ static void hash_one_table(const char *zTab){
           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;
         }
       }
@@ -354,18 +379,28 @@ static void hash_one_table(const char *zTab){
 ** 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);
@@ -382,43 +417,90 @@ int main(int argc, char **argv){
         showHelp();
         return 0;
       }else
-      if( strcmp(z,"primarykey")==0 ){
-        g.bSchemaPK = 1;
+      if( strcmp(z,"like")==0 ){
+        if( i==argc-1 ) cmdlineError("missing argument to %s", argv[i]);
+        if( zLike!=0 ) cmdlineError("only one --like allowed");
+        zLike = argv[++i];
+      }else
+      if( strcmp(z,"schema-only")==0 ){
+        omitContent = 1;
+      }else
+      if( strcmp(z,"without-schema")==0 ){
+        omitSchema = 1;
       }else
       {
         cmdlineError("unknown option: %s", argv[i]);
       }
-    }else if( zDb==0 ){
-      zDb = argv[i];
     }else{
-      cmdlineError("unknown argument: %s", argv[i]);
+      nFile++;
+      if( nFile<i ) argv[nFile] = argv[i];
     }
   }
-  if( zDb==0 ){
-    cmdlineError("database argument missing");
-  }
-  rc = sqlite3_open(zDb, &g.db);
-  if( rc ){
-    cmdlineError("cannot open database file \"%s\"", zDb);
+  if( nFile==0 ){
+    cmdlineError("no input files specified - nothing to do");
   }
-  rc = sqlite3_exec(g.db, "SELECT * FROM sqlite_master", 0, 0, &zErrMsg);
-  if( rc || zErrMsg ){
-    cmdlineError("\"%s\" does not appear to be a valid SQLite database", zDb);
+  if( omitSchema && omitContent ){
+    cmdlineError("only one of --without-schema and --omit-schema allowed");
   }
+  if( zLike==0 ) zLike = "%";
 
-  /* Handle tables one by one */
-  pStmt = db_prepare(
-    "SELECT name FROM sqlite_master\n"
-    " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
-    "UNION SELECT 'sqlite_master' AS name\n"
-    " ORDER BY name;\n"
-  );
-  hash_init();
-  while( SQLITE_ROW==sqlite3_step(pStmt) ){
-    hash_one_table((const char*)sqlite3_column_text(pStmt,0));
-  }
-  hash_finish();
+  for(i=1; i<=nFile; i++){
+    static const int openFlags = 
+       SQLITE_OPEN_READWRITE |     /* Read/write so hot journals can recover */
+       SQLITE_OPEN_URI
+    ;
+    zDb = argv[i];
+    rc = sqlite3_open_v2(zDb, &g.db, openFlags, 0);
+    if( rc ){
+      fprintf(stderr, "cannot open database file '%s'\n", zDb);
+      continue;
+    }
+    rc = sqlite3_exec(g.db, "SELECT * FROM sqlite_master", 0, 0, &zErrMsg);
+    if( rc || zErrMsg ){
+      sqlite3_close(g.db);
+      g.db = 0;
+      fprintf(stderr, "'%s' is not a valid SQLite database\n", zDb);
+      continue;
+    }
 
-  sqlite3_close(g.db);
+    /* Start the hash */
+    hash_init();
+  
+    /* Hash table content */
+    if( !omitContent ){
+      pStmt = db_prepare(
+        "SELECT name FROM sqlite_master\n"
+        " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
+        "   AND name NOT LIKE 'sqlite_%%'\n"
+        "   AND name LIKE '%q'\n"
+        " ORDER BY name COLLATE nocase;\n",
+        zLike
+      );
+      while( SQLITE_ROW==sqlite3_step(pStmt) ){
+        /* We want rows of the table to be hashed in PRIMARY KEY order.
+        ** Technically, an ORDER BY clause is required to guarantee that
+        ** order.  However, though not guaranteed by the documentation, every
+        ** historical version of SQLite has always output rows in PRIMARY KEY
+        ** order when there is no WHERE or GROUP BY clause, so the ORDER BY
+        ** can be safely omitted. */
+        hash_one_query("SELECT * FROM \"%w\"", sqlite3_column_text(pStmt,0));
+      }
+      sqlite3_finalize(pStmt);
+    }
+  
+    /* Hash the database schema */
+    if( !omitSchema ){
+      hash_one_query(
+         "SELECT type, name, tbl_name, sql FROM sqlite_master\n"
+         " WHERE tbl_name LIKE '%q'\n"
+         " ORDER BY name COLLATE nocase;\n",
+         zLike
+      );
+    }
+  
+    /* Finish and output the hash and close the database connection. */
+    hash_finish(zDb);
+    sqlite3_close(g.db);
+  }
   return 0;
 }