/ Check-in [88b22761]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add the --summary option to the sqldiff command-line tool.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 88b22761c59b06fa86c57f8d22a46046ad17d5d5
User & Date: drh 2015-04-14 19:01:08
Context
2015-04-15
04:10
An oversize hex literal can cause a parsing error while generating code for constants that are factored out of the main body of the VDBE program. So allow for that case. check-in: a084690b user: drh tags: trunk
2015-04-14
19:01
Add the --summary option to the sqldiff command-line tool. check-in: 88b22761 user: drh tags: trunk
15:14
Update API documentation to identify many functions as methods on objects. No changes to code. check-in: b549cbce user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to tool/sqldiff.c.

   352    352     }
   353    353     return az;
   354    354   }
   355    355   
   356    356   /*
   357    357   ** Print the sqlite3_value X as an SQL literal.
   358    358   */
   359         -static void printQuoted(sqlite3_value *X){
          359  +static void printQuoted(FILE *out, sqlite3_value *X){
   360    360     switch( sqlite3_value_type(X) ){
   361    361       case SQLITE_FLOAT: {
   362    362         double r1;
   363    363         char zBuf[50];
   364    364         r1 = sqlite3_value_double(X);
   365    365         sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
   366         -      printf("%s", zBuf);
          366  +      fprintf(out, "%s", zBuf);
   367    367         break;
   368    368       }
   369    369       case SQLITE_INTEGER: {
   370         -      printf("%lld", sqlite3_value_int64(X));
          370  +      fprintf(out, "%lld", sqlite3_value_int64(X));
   371    371         break;
   372    372       }
   373    373       case SQLITE_BLOB: {
   374    374         const unsigned char *zBlob = sqlite3_value_blob(X);
   375    375         int nBlob = sqlite3_value_bytes(X);
   376    376         if( zBlob ){
   377    377           int i;
   378         -        printf("x'");
          378  +        fprintf(out, "x'");
   379    379           for(i=0; i<nBlob; i++){
   380         -          printf("%02x", zBlob[i]);
          380  +          fprintf(out, "%02x", zBlob[i]);
   381    381           }
   382         -        printf("'");
          382  +        fprintf(out, "'");
   383    383         }else{
   384         -        printf("NULL");
          384  +        fprintf(out, "NULL");
   385    385         }
   386    386         break;
   387    387       }
   388    388       case SQLITE_TEXT: {
   389    389         const unsigned char *zArg = sqlite3_value_text(X);
   390    390         int i, j;
   391    391   
   392    392         if( zArg==0 ){
   393         -        printf("NULL");
          393  +        fprintf(out, "NULL");
   394    394         }else{
   395         -        printf("'");
          395  +        fprintf(out, "'");
   396    396           for(i=j=0; zArg[i]; i++){
   397    397             if( zArg[i]=='\'' ){
   398         -            printf("%.*s'", i-j+1, &zArg[j]);
          398  +            fprintf(out, "%.*s'", i-j+1, &zArg[j]);
   399    399               j = i+1;
   400    400             }
   401    401           }
   402         -        printf("%s'", &zArg[j]);
          402  +        fprintf(out, "%s'", &zArg[j]);
   403    403         }
   404    404         break;
   405    405       }
   406    406       case SQLITE_NULL: {
   407         -      printf("NULL");
          407  +      fprintf(out, "NULL");
   408    408         break;
   409    409       }
   410    410     }
   411    411   }
   412    412   
   413    413   /*
   414    414   ** Output SQL that will recreate the aux.zTab table.
   415    415   */
   416         -static void dump_table(const char *zTab){
          416  +static void dump_table(const char *zTab, FILE *out){
   417    417     char *zId = safeId(zTab); /* Name of the table */
   418    418     char **az = 0;            /* List of columns */
   419    419     int nPk;                  /* Number of true primary key columns */
   420    420     int nCol;                 /* Number of data columns */
   421    421     int i;                    /* Loop counter */
   422    422     sqlite3_stmt *pStmt;      /* SQL statement */
   423    423     const char *zSep;         /* Separator string */
   424    424     Str ins;                  /* Beginning of the INSERT statement */
   425    425   
   426    426     pStmt = db_prepare("SELECT sql FROM aux.sqlite_master WHERE name=%Q", zTab);
   427    427     if( SQLITE_ROW==sqlite3_step(pStmt) ){
   428         -    printf("%s;\n", sqlite3_column_text(pStmt,0));
          428  +    fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0));
   429    429     }
   430    430     sqlite3_finalize(pStmt);
   431    431     if( !g.bSchemaOnly ){
   432    432       az = columnNames("aux", zTab, &nPk);
   433    433       strInit(&ins);
   434    434       if( az==0 ){
   435    435         pStmt = db_prepare("SELECT * FROM aux.%s", zId);
................................................................................
   457    457           zSep = ",";
   458    458         }
   459    459         strPrintf(&ins,") VALUES");
   460    460         namelistFree(az);
   461    461       }
   462    462       nCol = sqlite3_column_count(pStmt);
   463    463       while( SQLITE_ROW==sqlite3_step(pStmt) ){
   464         -      printf("%s",ins.z);
          464  +      fprintf(out, "%s",ins.z);
   465    465         zSep = "(";
   466    466         for(i=0; i<nCol; i++){
   467         -        printf("%s",zSep);
   468         -        printQuoted(sqlite3_column_value(pStmt,i));
          467  +        fprintf(out, "%s",zSep);
          468  +        printQuoted(out, sqlite3_column_value(pStmt,i));
   469    469           zSep = ",";
   470    470         }
   471         -      printf(");\n");
          471  +      fprintf(out, ");\n");
   472    472       }
   473    473       sqlite3_finalize(pStmt);
   474    474       strFree(&ins);
   475    475     } /* endif !g.bSchemaOnly */
   476    476     pStmt = db_prepare("SELECT sql FROM aux.sqlite_master"
   477    477                        " WHERE type='index' AND tbl_name=%Q AND sql IS NOT NULL",
   478    478                        zTab);
   479    479     while( SQLITE_ROW==sqlite3_step(pStmt) ){
   480         -    printf("%s;\n", sqlite3_column_text(pStmt,0));
          480  +    fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0));
   481    481     }
   482    482     sqlite3_finalize(pStmt);
   483    483   }
   484    484   
   485    485   
   486    486   /*
   487    487   ** Compute all differences for a single table.
   488    488   */
   489         -static void diff_one_table(const char *zTab){
          489  +static void diff_one_table(const char *zTab, FILE *out){
   490    490     char *zId = safeId(zTab); /* Name of table (translated for us in SQL) */
   491    491     char **az = 0;            /* Columns in main */
   492    492     char **az2 = 0;           /* Columns in aux */
   493    493     int nPk;                  /* Primary key columns in main */
   494    494     int nPk2;                 /* Primary key columns in aux */
   495    495     int n;                    /* Number of columns in main */
   496    496     int n2;                   /* Number of columns in aux */
................................................................................
   520    520       goto end_diff_one_table;
   521    521     }
   522    522       
   523    523   
   524    524     if( sqlite3_table_column_metadata(g.db,"aux",zTab,0,0,0,0,0,0) ){
   525    525       if( !sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
   526    526         /* Table missing from second database. */
   527         -      printf("DROP TABLE %s;\n", zId);
          527  +      fprintf(out, "DROP TABLE %s;\n", zId);
   528    528       }
   529    529       goto end_diff_one_table;
   530    530     }
   531    531   
   532    532     if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
   533    533       /* Table missing from source */
   534         -    dump_table(zTab);
          534  +    dump_table(zTab, out);
   535    535       goto end_diff_one_table;
   536    536     }
   537    537   
   538    538     az = columnNames("main", zTab, &nPk);
   539    539     az2 = columnNames("aux", zTab, &nPk2);
   540    540     if( az && az2 ){
   541    541       for(n=0; az[n]; n++){
................................................................................
   544    544     }
   545    545     if( az==0
   546    546      || az2==0
   547    547      || nPk!=nPk2
   548    548      || az[n]
   549    549     ){
   550    550       /* Schema mismatch */
   551         -    printf("DROP TABLE %s;\n", zId);
   552         -    dump_table(zTab);
          551  +    fprintf(out, "DROP TABLE %s;\n", zId);
          552  +    dump_table(zTab, out);
   553    553       goto end_diff_one_table;
   554    554     }
   555    555   
   556    556     /* Build the comparison query */
   557    557     for(n2=n; az[n2]; n2++){}
   558    558     nQ = nPk2+1+2*(n2-nPk2);
   559    559     if( n2>nPk2 ){
................................................................................
   638    638       "   AND sql IS NOT NULL"
   639    639       "   AND sql NOT IN (SELECT sql FROM aux.sqlite_master"
   640    640       "                    WHERE type='index' AND tbl_name=%Q"
   641    641       "                      AND sql IS NOT NULL)",
   642    642       zTab, zTab);
   643    643     while( SQLITE_ROW==sqlite3_step(pStmt) ){
   644    644       char *z = safeId((const char*)sqlite3_column_text(pStmt,0));
   645         -    printf("DROP INDEX %s;\n", z);
          645  +    fprintf(out, "DROP INDEX %s;\n", z);
   646    646       sqlite3_free(z);
   647    647     }
   648    648     sqlite3_finalize(pStmt);
   649    649   
   650    650     /* Run the query and output differences */
   651    651     if( !g.bSchemaOnly ){
   652    652       pStmt = db_prepare(sql.z);
   653    653       while( SQLITE_ROW==sqlite3_step(pStmt) ){
   654    654         int iType = sqlite3_column_int(pStmt, nPk);
   655    655         if( iType==1 || iType==2 ){
   656    656           if( iType==1 ){       /* Change the content of a row */
   657         -          printf("UPDATE %s", zId);
          657  +          fprintf(out, "UPDATE %s", zId);
   658    658             zSep = " SET";
   659    659             for(i=nPk+1; i<nQ; i+=2){
   660    660               if( sqlite3_column_int(pStmt,i)==0 ) continue;
   661         -            printf("%s %s=", zSep, az2[(i+nPk-1)/2]);
          661  +            fprintf(out, "%s %s=", zSep, az2[(i+nPk-1)/2]);
   662    662               zSep = ",";
   663         -            printQuoted(sqlite3_column_value(pStmt,i+1));
          663  +            printQuoted(out, sqlite3_column_value(pStmt,i+1));
   664    664             }
   665    665           }else{                /* Delete a row */
   666         -          printf("DELETE FROM %s", zId);
          666  +          fprintf(out, "DELETE FROM %s", zId);
   667    667           }
   668    668           zSep = " WHERE";
   669    669           for(i=0; i<nPk; i++){
   670         -          printf("%s %s=", zSep, az2[i]);
   671         -          printQuoted(sqlite3_column_value(pStmt,i));
          670  +          fprintf(out, "%s %s=", zSep, az2[i]);
          671  +          printQuoted(out, sqlite3_column_value(pStmt,i));
   672    672             zSep = ",";
   673    673           }
   674         -        printf(";\n");
          674  +        fprintf(out, ";\n");
   675    675         }else{                  /* Insert a row */
   676         -        printf("INSERT INTO %s(%s", zId, az2[0]);
   677         -        for(i=1; az2[i]; i++) printf(",%s", az2[i]);
   678         -        printf(") VALUES");
          676  +        fprintf(out, "INSERT INTO %s(%s", zId, az2[0]);
          677  +        for(i=1; az2[i]; i++) fprintf(out, ",%s", az2[i]);
          678  +        fprintf(out, ") VALUES");
   679    679           zSep = "(";
   680    680           for(i=0; i<nPk2; i++){
   681         -          printf("%s", zSep);
          681  +          fprintf(out, "%s", zSep);
   682    682             zSep = ",";
   683         -          printQuoted(sqlite3_column_value(pStmt,i));
          683  +          printQuoted(out, sqlite3_column_value(pStmt,i));
   684    684           }
   685    685           for(i=nPk2+2; i<nQ; i+=2){
   686         -          printf(",");
   687         -          printQuoted(sqlite3_column_value(pStmt,i));
          686  +          fprintf(out, ",");
          687  +          printQuoted(out, sqlite3_column_value(pStmt,i));
   688    688           }
   689         -        printf(");\n");
          689  +        fprintf(out, ");\n");
   690    690         }
   691    691       }
   692    692       sqlite3_finalize(pStmt);
   693    693     } /* endif !g.bSchemaOnly */
   694    694   
   695    695     /* Create indexes that are missing in the source */
   696    696     pStmt = db_prepare(
................................................................................
   698    698       " WHERE type='index' AND tbl_name=%Q"
   699    699       "   AND sql IS NOT NULL"
   700    700       "   AND sql NOT IN (SELECT sql FROM main.sqlite_master"
   701    701       "                    WHERE type='index' AND tbl_name=%Q"
   702    702       "                      AND sql IS NOT NULL)",
   703    703       zTab, zTab);
   704    704     while( SQLITE_ROW==sqlite3_step(pStmt) ){
   705         -    printf("%s;\n", sqlite3_column_text(pStmt,0));
          705  +    fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0));
   706    706     }
   707    707     sqlite3_finalize(pStmt);
   708    708   
   709    709   end_diff_one_table:
   710    710     strFree(&sql);
          711  +  sqlite3_free(zId);
          712  +  namelistFree(az);
          713  +  namelistFree(az2);
          714  +  return;
          715  +}
          716  +
          717  +/*
          718  +** Display a summary of differences between two versions of the same
          719  +** table table.
          720  +**
          721  +**   *  Number of rows changed
          722  +**   *  Number of rows added
          723  +**   *  Number of rows deleted
          724  +**   *  Number of identical rows
          725  +*/
          726  +static void summarize_one_table(const char *zTab, FILE *out){
          727  +  char *zId = safeId(zTab); /* Name of table (translated for us in SQL) */
          728  +  char **az = 0;            /* Columns in main */
          729  +  char **az2 = 0;           /* Columns in aux */
          730  +  int nPk;                  /* Primary key columns in main */
          731  +  int nPk2;                 /* Primary key columns in aux */
          732  +  int n;                    /* Number of columns in main */
          733  +  int n2;                   /* Number of columns in aux */
          734  +  int i;                    /* Loop counter */
          735  +  const char *zSep;         /* Separator string */
          736  +  Str sql;                  /* Comparison query */
          737  +  sqlite3_stmt *pStmt;      /* Query statement to do the diff */
          738  +  sqlite3_int64 nUpdate;    /* Number of updated rows */
          739  +  sqlite3_int64 nUnchanged; /* Number of unmodified rows */
          740  +  sqlite3_int64 nDelete;    /* Number of deleted rows */
          741  +  sqlite3_int64 nInsert;    /* Number of inserted rows */
          742  +
          743  +  strInit(&sql);
          744  +  if( sqlite3_table_column_metadata(g.db,"aux",zTab,0,0,0,0,0,0) ){
          745  +    if( !sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
          746  +      /* Table missing from second database. */
          747  +      fprintf(out, "%s: missing from second database\n", zTab);
          748  +    }
          749  +    goto end_summarize_one_table;
          750  +  }
          751  +
          752  +  if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){
          753  +    /* Table missing from source */
          754  +    fprintf(out, "%s: missing from first database\n", zTab);
          755  +    goto end_summarize_one_table;
          756  +  }
          757  +
          758  +  az = columnNames("main", zTab, &nPk);
          759  +  az2 = columnNames("aux", zTab, &nPk2);
          760  +  if( az && az2 ){
          761  +    for(n=0; az[n]; n++){
          762  +      if( sqlite3_stricmp(az[n],az2[n])!=0 ) break;
          763  +    }
          764  +  }
          765  +  if( az==0
          766  +   || az2==0
          767  +   || nPk!=nPk2
          768  +   || az[n]
          769  +  ){
          770  +    /* Schema mismatch */
          771  +    fprintf(out, "%s: incompatible schema\n", zTab);
          772  +    goto end_summarize_one_table;
          773  +  }
          774  +
          775  +  /* Build the comparison query */
          776  +  for(n2=n; az[n2]; n2++){}
          777  +  strPrintf(&sql, "SELECT 1, count(*)");
          778  +  if( n2==nPk2 ){
          779  +    strPrintf(&sql, ", 0\n");
          780  +  }else{
          781  +    zSep = ", sum(";
          782  +    for(i=nPk; az[i]; i++){
          783  +      strPrintf(&sql, "%sA.%s IS NOT B.%s", zSep, az[i], az[i]);
          784  +      zSep = " OR ";
          785  +    }
          786  +    strPrintf(&sql, ")\n");
          787  +  }
          788  +  strPrintf(&sql, "  FROM main.%s A, aux.%s B\n", zId, zId);
          789  +  zSep = " WHERE";
          790  +  for(i=0; i<nPk; i++){
          791  +    strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
          792  +    zSep = " AND";
          793  +  }
          794  +  strPrintf(&sql, " UNION ALL\n");
          795  +  strPrintf(&sql, "SELECT 2, count(*), 0\n");
          796  +  strPrintf(&sql, "  FROM main.%s A\n", zId);
          797  +  strPrintf(&sql, " WHERE NOT EXISTS(SELECT 1 FROM aux.%s B ", zId);
          798  +  zSep = "WHERE";
          799  +  for(i=0; i<nPk; i++){
          800  +    strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
          801  +    zSep = " AND";
          802  +  }
          803  +  strPrintf(&sql, ")\n");
          804  +  strPrintf(&sql, " UNION ALL\n");
          805  +  strPrintf(&sql, "SELECT 3, count(*), 0\n");
          806  +  strPrintf(&sql, "  FROM aux.%s B\n", zId);
          807  +  strPrintf(&sql, " WHERE NOT EXISTS(SELECT 1 FROM main.%s A ", zId);
          808  +  zSep = "WHERE";
          809  +  for(i=0; i<nPk; i++){
          810  +    strPrintf(&sql, "%s A.%s=B.%s", zSep, az[i], az[i]);
          811  +    zSep = " AND";
          812  +  }
          813  +  strPrintf(&sql, ")\n ORDER BY 1;\n");
          814  +
          815  +  if( (g.fDebug & DEBUG_DIFF_SQL)!=0 ){ 
          816  +    printf("SQL for %s:\n%s\n", zId, sql.z);
          817  +    goto end_summarize_one_table;
          818  +  }
          819  +
          820  +  /* Run the query and output difference summary */
          821  +  pStmt = db_prepare(sql.z);
          822  +  nUpdate = 0;
          823  +  nInsert = 0;
          824  +  nDelete = 0;
          825  +  nUnchanged = 0;
          826  +  while( SQLITE_ROW==sqlite3_step(pStmt) ){
          827  +    switch( sqlite3_column_int(pStmt,0) ){
          828  +      case 1:
          829  +        nUpdate = sqlite3_column_int64(pStmt,2);
          830  +        nUnchanged = sqlite3_column_int64(pStmt,1) - nUpdate;
          831  +        break;
          832  +      case 2:
          833  +        nDelete = sqlite3_column_int64(pStmt,1);
          834  +        break;
          835  +      case 3:
          836  +        nInsert = sqlite3_column_int64(pStmt,1);
          837  +        break;
          838  +    }
          839  +  }
          840  +  sqlite3_finalize(pStmt);
          841  +  fprintf(out, "%s: %lld changes, %lld inserts, %lld deletes, %lld unchanged\n",
          842  +          zTab, nUpdate, nInsert, nDelete, nUnchanged);
          843  +
          844  +end_summarize_one_table:
          845  +  strFree(&sql);
   711    846     sqlite3_free(zId);
   712    847     namelistFree(az);
   713    848     namelistFree(az2);
   714    849     return;
   715    850   }
   716    851   
   717    852   /*
................................................................................
   974   1109     printf("Usage: %s [options] DB1 DB2\n", g.zArgv0);
   975   1110     printf(
   976   1111   "Output SQL text that would transform DB1 into DB2.\n"
   977   1112   "Options:\n"
   978   1113   "  --changeset FILE      Write a CHANGESET into FILE\n"
   979   1114   "  --primarykey          Use schema-defined PRIMARY KEYs\n"
   980   1115   "  --schema              Show only differences in the schema\n"
         1116  +"  --summary             Show only a summary of the differences\n"
   981   1117   "  --table TAB           Show only differences in table TAB\n"
   982   1118     );
   983   1119   }
   984   1120   
   985   1121   int main(int argc, char **argv){
   986   1122     const char *zDb1 = 0;
   987   1123     const char *zDb2 = 0;
   988   1124     int i;
   989   1125     int rc;
   990   1126     char *zErrMsg = 0;
   991   1127     char *zSql;
   992   1128     sqlite3_stmt *pStmt;
   993   1129     char *zTab = 0;
   994         -  FILE *out = 0;
         1130  +  FILE *out = stdout;
         1131  +  void (*xDiff)(const char*,FILE*) = diff_one_table;
   995   1132   
   996   1133     g.zArgv0 = argv[0];
   997   1134     for(i=1; i<argc; i++){
   998   1135       const char *z = argv[i];
   999   1136       if( z[0]=='-' ){
  1000   1137         z++;
  1001   1138         if( z[0]=='-' ) z++;
  1002   1139         if( strcmp(z,"changeset")==0 ){
  1003   1140           out = fopen(argv[++i], "wb");
  1004   1141           if( out==0 ) cmdlineError("cannot open: %s", argv[i]);
         1142  +        xDiff = changeset_one_table;
  1005   1143         }else
  1006   1144         if( strcmp(z,"debug")==0 ){
  1007   1145           g.fDebug = strtol(argv[++i], 0, 0);
  1008   1146         }else
  1009   1147         if( strcmp(z,"help")==0 ){
  1010   1148           showHelp();
  1011   1149           return 0;
................................................................................
  1012   1150         }else
  1013   1151         if( strcmp(z,"primarykey")==0 ){
  1014   1152           g.bSchemaPK = 1;
  1015   1153         }else
  1016   1154         if( strcmp(z,"schema")==0 ){
  1017   1155           g.bSchemaOnly = 1;
  1018   1156         }else
         1157  +      if( strcmp(z,"summary")==0 ){
         1158  +        xDiff = summarize_one_table;
         1159  +      }else
  1019   1160         if( strcmp(z,"table")==0 ){
  1020   1161           zTab = argv[++i];
  1021   1162         }else
  1022   1163         {
  1023   1164           cmdlineError("unknown option: %s", argv[i]);
  1024   1165         }
  1025   1166       }else if( zDb1==0 ){
................................................................................
  1048   1189     }
  1049   1190     rc = sqlite3_exec(g.db, "SELECT * FROM aux.sqlite_master", 0, 0, &zErrMsg);
  1050   1191     if( rc || zErrMsg ){
  1051   1192       cmdlineError("\"%s\" does not appear to be a valid SQLite database", zDb2);
  1052   1193     }
  1053   1194   
  1054   1195     if( zTab ){
  1055         -    if( out ){
  1056         -      changeset_one_table(zTab, out);
  1057         -    }else{
  1058         -      diff_one_table(zTab);
  1059         -    }
         1196  +    xDiff(zTab, out);
  1060   1197     }else{
  1061   1198       /* Handle tables one by one */
  1062   1199       pStmt = db_prepare(
  1063   1200         "SELECT name FROM main.sqlite_master\n"
  1064   1201         " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
  1065   1202         " UNION\n"
  1066   1203         "SELECT name FROM aux.sqlite_master\n"
  1067   1204         " WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
  1068   1205         " ORDER BY name"
  1069   1206       );
  1070   1207       while( SQLITE_ROW==sqlite3_step(pStmt) ){
  1071         -      const char *zTab = (const char*)sqlite3_column_text(pStmt,0);
  1072         -      if( out ){
  1073         -        changeset_one_table(zTab, out);
  1074         -      }else{
  1075         -        diff_one_table(zTab);
  1076         -      }
         1208  +      xDiff((const char*)sqlite3_column_text(pStmt,0), out);
  1077   1209       }
  1078   1210       sqlite3_finalize(pStmt);
  1079   1211     }
  1080   1212   
  1081   1213     /* TBD: Handle trigger differences */
  1082   1214     /* TBD: Handle view differences */
  1083   1215     sqlite3_close(g.db);
  1084   1216     return 0;
  1085   1217   }