]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the --summary option to the sqldiff command-line tool.
authordrh <drh@noemail.net>
Tue, 14 Apr 2015 19:01:08 +0000 (19:01 +0000)
committerdrh <drh@noemail.net>
Tue, 14 Apr 2015 19:01:08 +0000 (19:01 +0000)
FossilOrigin-Name: 88b22761c59b06fa86c57f8d22a46046ad17d5d5

manifest
manifest.uuid
tool/sqldiff.c

index c795bef110f9008340d7eda0b01ea888463272ad..821eb96f29c50c1bfe41ee4398e01f848cc582e1 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Update\sAPI\sdocumentation\sto\sidentify\smany\sfunctions\sas\smethods\son\sobjects.\nNo\schanges\sto\scode.
-D 2015-04-14T15:14:06.177
+C Add\sthe\s--summary\soption\sto\sthe\ssqldiff\scommand-line\stool.
+D 2015-04-14T19:01:08.549
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 5f78b1ab81b64e7c57a75d170832443e66c0880a
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -1239,7 +1239,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
-F tool/sqldiff.c 51c05cc1435507736b8b5a41a0498016041b3e48
+F tool/sqldiff.c 5c16cf3a1f566873abbdecac0d13a6691437564f
 F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
 F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
 F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f
@@ -1250,7 +1250,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 211411d02c0729c9af0e3cc7e4910db2e7e0d08e
-R 32297e2ca295bdee2feb4a534c04bc42
+P b549cbcee1c11f9ffedf763ca672b125eac87bfe
+R 64950626ad311356247b51bbef2cd09d
 U drh
-Z 358af9cd25fc843b833aaa97a1900951
+Z 2d35d7a2b23bff60413bdff46818cee3
index 5721ce9565087acb984f1329d972140bf64a3980..674f6cdaa237f27e416f0f1f9ac4d50d64cfe970 100644 (file)
@@ -1 +1 @@
-b549cbcee1c11f9ffedf763ca672b125eac87bfe
\ No newline at end of file
+88b22761c59b06fa86c57f8d22a46046ad17d5d5
\ No newline at end of file
index 4455c582efc8857797242986a9b763122ad9f8a8..c7b59400dfd45a17968c69c61ff4474a9ceefa1e 100644 (file)
@@ -356,18 +356,18 @@ static char **columnNames(const char *zDb, const char *zTab, int *pnPKey){
 /*
 ** Print the sqlite3_value X as an SQL literal.
 */
-static void printQuoted(sqlite3_value *X){
+static void printQuoted(FILE *out, sqlite3_value *X){
   switch( sqlite3_value_type(X) ){
     case SQLITE_FLOAT: {
       double r1;
       char zBuf[50];
       r1 = sqlite3_value_double(X);
       sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
-      printf("%s", zBuf);
+      fprintf(out, "%s", zBuf);
       break;
     }
     case SQLITE_INTEGER: {
-      printf("%lld", sqlite3_value_int64(X));
+      fprintf(out, "%lld", sqlite3_value_int64(X));
       break;
     }
     case SQLITE_BLOB: {
@@ -375,13 +375,13 @@ static void printQuoted(sqlite3_value *X){
       int nBlob = sqlite3_value_bytes(X);
       if( zBlob ){
         int i;
-        printf("x'");
+        fprintf(out, "x'");
         for(i=0; i<nBlob; i++){
-          printf("%02x", zBlob[i]);
+          fprintf(out, "%02x", zBlob[i]);
         }
-        printf("'");
+        fprintf(out, "'");
       }else{
-        printf("NULL");
+        fprintf(out, "NULL");
       }
       break;
     }
@@ -390,21 +390,21 @@ static void printQuoted(sqlite3_value *X){
       int i, j;
 
       if( zArg==0 ){
-        printf("NULL");
+        fprintf(out, "NULL");
       }else{
-        printf("'");
+        fprintf(out, "'");
         for(i=j=0; zArg[i]; i++){
           if( zArg[i]=='\'' ){
-            printf("%.*s'", i-j+1, &zArg[j]);
+            fprintf(out, "%.*s'", i-j+1, &zArg[j]);
             j = i+1;
           }
         }
-        printf("%s'", &zArg[j]);
+        fprintf(out, "%s'", &zArg[j]);
       }
       break;
     }
     case SQLITE_NULL: {
-      printf("NULL");
+      fprintf(out, "NULL");
       break;
     }
   }
@@ -413,7 +413,7 @@ static void printQuoted(sqlite3_value *X){
 /*
 ** Output SQL that will recreate the aux.zTab table.
 */
-static void dump_table(const char *zTab){
+static void dump_table(const char *zTab, FILE *out){
   char *zId = safeId(zTab); /* Name of the table */
   char **az = 0;            /* List of columns */
   int nPk;                  /* Number of true primary key columns */
@@ -425,7 +425,7 @@ static void dump_table(const char *zTab){
 
   pStmt = db_prepare("SELECT sql FROM aux.sqlite_master WHERE name=%Q", zTab);
   if( SQLITE_ROW==sqlite3_step(pStmt) ){
-    printf("%s;\n", sqlite3_column_text(pStmt,0));
+    fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0));
   }
   sqlite3_finalize(pStmt);
   if( !g.bSchemaOnly ){
@@ -461,14 +461,14 @@ static void dump_table(const char *zTab){
     }
     nCol = sqlite3_column_count(pStmt);
     while( SQLITE_ROW==sqlite3_step(pStmt) ){
-      printf("%s",ins.z);
+      fprintf(out, "%s",ins.z);
       zSep = "(";
       for(i=0; i<nCol; i++){
-        printf("%s",zSep);
-        printQuoted(sqlite3_column_value(pStmt,i));
+        fprintf(out, "%s",zSep);
+        printQuoted(out, sqlite3_column_value(pStmt,i));
         zSep = ",";
       }
-      printf(");\n");
+      fprintf(out, ");\n");
     }
     sqlite3_finalize(pStmt);
     strFree(&ins);
@@ -477,7 +477,7 @@ static void dump_table(const char *zTab){
                      " WHERE type='index' AND tbl_name=%Q AND sql IS NOT NULL",
                      zTab);
   while( SQLITE_ROW==sqlite3_step(pStmt) ){
-    printf("%s;\n", sqlite3_column_text(pStmt,0));
+    fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0));
   }
   sqlite3_finalize(pStmt);
 }
@@ -486,7 +486,7 @@ static void dump_table(const char *zTab){
 /*
 ** Compute all differences for a single table.
 */
-static void diff_one_table(const char *zTab){
+static void diff_one_table(const char *zTab, FILE *out){
   char *zId = safeId(zTab); /* Name of table (translated for us in SQL) */
   char **az = 0;            /* Columns in main */
   char **az2 = 0;           /* Columns in aux */
@@ -524,14 +524,14 @@ static void diff_one_table(const char *zTab){
   if( sqlite3_table_column_metadata(g.db,"aux",zTab,0,0,0,0,0,0) ){
     if( !sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
       /* Table missing from second database. */
-      printf("DROP TABLE %s;\n", zId);
+      fprintf(out, "DROP TABLE %s;\n", zId);
     }
     goto end_diff_one_table;
   }
 
   if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
     /* Table missing from source */
-    dump_table(zTab);
+    dump_table(zTab, out);
     goto end_diff_one_table;
   }
 
@@ -548,8 +548,8 @@ static void diff_one_table(const char *zTab){
    || az[n]
   ){
     /* Schema mismatch */
-    printf("DROP TABLE %s;\n", zId);
-    dump_table(zTab);
+    fprintf(out, "DROP TABLE %s;\n", zId);
+    dump_table(zTab, out);
     goto end_diff_one_table;
   }
 
@@ -642,7 +642,7 @@ static void diff_one_table(const char *zTab){
     zTab, zTab);
   while( SQLITE_ROW==sqlite3_step(pStmt) ){
     char *z = safeId((const char*)sqlite3_column_text(pStmt,0));
-    printf("DROP INDEX %s;\n", z);
+    fprintf(out, "DROP INDEX %s;\n", z);
     sqlite3_free(z);
   }
   sqlite3_finalize(pStmt);
@@ -654,39 +654,39 @@ static void diff_one_table(const char *zTab){
       int iType = sqlite3_column_int(pStmt, nPk);
       if( iType==1 || iType==2 ){
         if( iType==1 ){       /* Change the content of a row */
-          printf("UPDATE %s", zId);
+          fprintf(out, "UPDATE %s", zId);
           zSep = " SET";
           for(i=nPk+1; i<nQ; i+=2){
             if( sqlite3_column_int(pStmt,i)==0 ) continue;
-            printf("%s %s=", zSep, az2[(i+nPk-1)/2]);
+            fprintf(out, "%s %s=", zSep, az2[(i+nPk-1)/2]);
             zSep = ",";
-            printQuoted(sqlite3_column_value(pStmt,i+1));
+            printQuoted(out, sqlite3_column_value(pStmt,i+1));
           }
         }else{                /* Delete a row */
-          printf("DELETE FROM %s", zId);
+          fprintf(out, "DELETE FROM %s", zId);
         }
         zSep = " WHERE";
         for(i=0; i<nPk; i++){
-          printf("%s %s=", zSep, az2[i]);
-          printQuoted(sqlite3_column_value(pStmt,i));
+          fprintf(out, "%s %s=", zSep, az2[i]);
+          printQuoted(out, sqlite3_column_value(pStmt,i));
           zSep = ",";
         }
-        printf(";\n");
+        fprintf(out, ";\n");
       }else{                  /* Insert a row */
-        printf("INSERT INTO %s(%s", zId, az2[0]);
-        for(i=1; az2[i]; i++) printf(",%s", az2[i]);
-        printf(") VALUES");
+        fprintf(out, "INSERT INTO %s(%s", zId, az2[0]);
+        for(i=1; az2[i]; i++) fprintf(out, ",%s", az2[i]);
+        fprintf(out, ") VALUES");
         zSep = "(";
         for(i=0; i<nPk2; i++){
-          printf("%s", zSep);
+          fprintf(out, "%s", zSep);
           zSep = ",";
-          printQuoted(sqlite3_column_value(pStmt,i));
+          printQuoted(out, sqlite3_column_value(pStmt,i));
         }
         for(i=nPk2+2; i<nQ; i+=2){
-          printf(",");
-          printQuoted(sqlite3_column_value(pStmt,i));
+          fprintf(out, ",");
+          printQuoted(out, sqlite3_column_value(pStmt,i));
         }
-        printf(");\n");
+        fprintf(out, ");\n");
       }
     }
     sqlite3_finalize(pStmt);
@@ -702,7 +702,7 @@ static void diff_one_table(const char *zTab){
     "                      AND sql IS NOT NULL)",
     zTab, zTab);
   while( SQLITE_ROW==sqlite3_step(pStmt) ){
-    printf("%s;\n", sqlite3_column_text(pStmt,0));
+    fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0));
   }
   sqlite3_finalize(pStmt);
 
@@ -714,6 +714,141 @@ end_diff_one_table:
   return;
 }
 
+/*
+** Display a summary of differences between two versions of the same
+** table table.
+**
+**   *  Number of rows changed
+**   *  Number of rows added
+**   *  Number of rows deleted
+**   *  Number of identical rows
+*/
+static void summarize_one_table(const char *zTab, FILE *out){
+  char *zId = safeId(zTab); /* Name of table (translated for us in SQL) */
+  char **az = 0;            /* Columns in main */
+  char **az2 = 0;           /* Columns in aux */
+  int nPk;                  /* Primary key columns in main */
+  int nPk2;                 /* Primary key columns in aux */
+  int n;                    /* Number of columns in main */
+  int n2;                   /* Number of columns in aux */
+  int i;                    /* Loop counter */
+  const char *zSep;         /* Separator string */
+  Str sql;                  /* Comparison query */
+  sqlite3_stmt *pStmt;      /* Query statement to do the diff */
+  sqlite3_int64 nUpdate;    /* Number of updated rows */
+  sqlite3_int64 nUnchanged; /* Number of unmodified rows */
+  sqlite3_int64 nDelete;    /* Number of deleted rows */
+  sqlite3_int64 nInsert;    /* Number of inserted rows */
+
+  strInit(&sql);
+  if( sqlite3_table_column_metadata(g.db,"aux",zTab,0,0,0,0,0,0) ){
+    if( !sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
+      /* Table missing from second database. */
+      fprintf(out, "%s: missing from second database\n", zTab);
+    }
+    goto end_summarize_one_table;
+  }
+
+  if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
+    /* Table missing from source */
+    fprintf(out, "%s: missing from first database\n", zTab);
+    goto end_summarize_one_table;
+  }
+
+  az = columnNames("main", zTab, &nPk);
+  az2 = columnNames("aux", zTab, &nPk2);
+  if( az && az2 ){
+    for(n=0; az[n]; n++){
+      if( sqlite3_stricmp(az[n],az2[n])!=0 ) break;
+    }
+  }
+  if( az==0
+   || az2==0
+   || nPk!=nPk2
+   || az[n]
+  ){
+    /* Schema mismatch */
+    fprintf(out, "%s: incompatible schema\n", zTab);
+    goto end_summarize_one_table;
+  }
+
+  /* Build the comparison query */
+  for(n2=n; az[n2]; n2++){}
+  strPrintf(&sql, "SELECT 1, count(*)");
+  if( n2==nPk2 ){
+    strPrintf(&sql, ", 0\n");
+  }else{
+    zSep = ", sum(";
+    for(i=nPk; az[i]; i++){
+      strPrintf(&sql, "%sA.%s IS NOT B.%s", zSep, az[i], az[i]);
+      zSep = " OR ";
+    }
+    strPrintf(&sql, ")\n");
+  }
+  strPrintf(&sql, "  FROM main.%s A, aux.%s B\n", zId, zId);
+  zSep = " WHERE";
+  for(i=0; i<nPk; i++){
+    strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
+    zSep = " AND";
+  }
+  strPrintf(&sql, " UNION ALL\n");
+  strPrintf(&sql, "SELECT 2, count(*), 0\n");
+  strPrintf(&sql, "  FROM main.%s A\n", zId);
+  strPrintf(&sql, " WHERE NOT EXISTS(SELECT 1 FROM aux.%s B ", zId);
+  zSep = "WHERE";
+  for(i=0; i<nPk; i++){
+    strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
+    zSep = " AND";
+  }
+  strPrintf(&sql, ")\n");
+  strPrintf(&sql, " UNION ALL\n");
+  strPrintf(&sql, "SELECT 3, count(*), 0\n");
+  strPrintf(&sql, "  FROM aux.%s B\n", zId);
+  strPrintf(&sql, " WHERE NOT EXISTS(SELECT 1 FROM main.%s A ", zId);
+  zSep = "WHERE";
+  for(i=0; i<nPk; i++){
+    strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
+    zSep = " AND";
+  }
+  strPrintf(&sql, ")\n ORDER BY 1;\n");
+
+  if( (g.fDebug & DEBUG_DIFF_SQL)!=0 ){ 
+    printf("SQL for %s:\n%s\n", zId, sql.z);
+    goto end_summarize_one_table;
+  }
+
+  /* Run the query and output difference summary */
+  pStmt = db_prepare(sql.z);
+  nUpdate = 0;
+  nInsert = 0;
+  nDelete = 0;
+  nUnchanged = 0;
+  while( SQLITE_ROW==sqlite3_step(pStmt) ){
+    switch( sqlite3_column_int(pStmt,0) ){
+      case 1:
+        nUpdate = sqlite3_column_int64(pStmt,2);
+        nUnchanged = sqlite3_column_int64(pStmt,1) - nUpdate;
+        break;
+      case 2:
+        nDelete = sqlite3_column_int64(pStmt,1);
+        break;
+      case 3:
+        nInsert = sqlite3_column_int64(pStmt,1);
+        break;
+    }
+  }
+  sqlite3_finalize(pStmt);
+  fprintf(out, "%s: %lld changes, %lld inserts, %lld deletes, %lld unchanged\n",
+          zTab, nUpdate, nInsert, nDelete, nUnchanged);
+
+end_summarize_one_table:
+  strFree(&sql);
+  sqlite3_free(zId);
+  namelistFree(az);
+  namelistFree(az2);
+  return;
+}
+
 /*
 ** Write a 64-bit signed integer as a varint onto out
 */
@@ -978,6 +1113,7 @@ static void showHelp(void){
 "  --changeset FILE      Write a CHANGESET into FILE\n"
 "  --primarykey          Use schema-defined PRIMARY KEYs\n"
 "  --schema              Show only differences in the schema\n"
+"  --summary             Show only a summary of the differences\n"
 "  --table TAB           Show only differences in table TAB\n"
   );
 }
@@ -991,7 +1127,8 @@ int main(int argc, char **argv){
   char *zSql;
   sqlite3_stmt *pStmt;
   char *zTab = 0;
-  FILE *out = 0;
+  FILE *out = stdout;
+  void (*xDiff)(const char*,FILE*) = diff_one_table;
 
   g.zArgv0 = argv[0];
   for(i=1; i<argc; i++){
@@ -1002,6 +1139,7 @@ int main(int argc, char **argv){
       if( strcmp(z,"changeset")==0 ){
         out = fopen(argv[++i], "wb");
         if( out==0 ) cmdlineError("cannot open: %s", argv[i]);
+        xDiff = changeset_one_table;
       }else
       if( strcmp(z,"debug")==0 ){
         g.fDebug = strtol(argv[++i], 0, 0);
@@ -1016,6 +1154,9 @@ int main(int argc, char **argv){
       if( strcmp(z,"schema")==0 ){
         g.bSchemaOnly = 1;
       }else
+      if( strcmp(z,"summary")==0 ){
+        xDiff = summarize_one_table;
+      }else
       if( strcmp(z,"table")==0 ){
         zTab = argv[++i];
       }else
@@ -1052,11 +1193,7 @@ int main(int argc, char **argv){
   }
 
   if( zTab ){
-    if( out ){
-      changeset_one_table(zTab, out);
-    }else{
-      diff_one_table(zTab);
-    }
+    xDiff(zTab, out);
   }else{
     /* Handle tables one by one */
     pStmt = db_prepare(
@@ -1068,12 +1205,7 @@ int main(int argc, char **argv){
       " ORDER BY name"
     );
     while( SQLITE_ROW==sqlite3_step(pStmt) ){
-      const char *zTab = (const char*)sqlite3_column_text(pStmt,0);
-      if( out ){
-        changeset_one_table(zTab, out);
-      }else{
-        diff_one_table(zTab);
-      }
+      xDiff((const char*)sqlite3_column_text(pStmt,0), out);
     }
     sqlite3_finalize(pStmt);
   }