modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/session/changeset.c (about)

     1  /*
     2  ** 2014-08-18
     3  **
     4  ** The author disclaims copyright to this source code.  In place of
     5  ** a legal notice, here is a blessing:
     6  **
     7  **    May you do good and not evil.
     8  **    May you find forgiveness for yourself and forgive others.
     9  **    May you share freely, never taking more than you give.
    10  **
    11  *************************************************************************
    12  ** This file contains code to implement the "changeset" command line
    13  ** utility for displaying and transforming changesets generated by
    14  ** the Sessions extension.
    15  */
    16  #include "sqlite3.h"
    17  #include <stdio.h>
    18  #include <stdlib.h>
    19  #include <string.h>
    20  #include <assert.h>
    21  #include <ctype.h>
    22  
    23  
    24  /*
    25  ** Show a usage message on stderr then quit.
    26  */
    27  static void usage(const char *argv0){
    28    fprintf(stderr, "Usage: %s FILENAME COMMAND ...\n", argv0);
    29    fprintf(stderr,
    30      "COMMANDs:\n"
    31      "   apply DB           Apply the changeset to database file DB\n"
    32      "   concat FILE2 OUT   Concatenate FILENAME and FILE2 into OUT\n"
    33      "   dump               Show the complete content of the changeset\n"
    34      "   invert OUT         Write an inverted changeset into file OUT\n"
    35      "   sql                Give a pseudo-SQL rendering of the changeset\n"
    36    );
    37    exit(1);
    38  }
    39  
    40  /*
    41  ** Read the content of a disk file into an in-memory buffer
    42  */
    43  static void readFile(const char *zFilename, int *pSz, void **ppBuf){
    44    FILE *f;
    45    int sz;
    46    void *pBuf;
    47    f = fopen(zFilename, "rb");
    48    if( f==0 ){
    49      fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
    50      exit(1);
    51    }
    52    fseek(f, 0, SEEK_END);
    53    sz = (int)ftell(f);
    54    rewind(f);
    55    pBuf = sqlite3_malloc( sz ? sz : 1 );
    56    if( pBuf==0 ){
    57      fprintf(stderr, "cannot allocate %d to hold content of \"%s\"\n",
    58              sz, zFilename);
    59      exit(1);
    60    }
    61    if( sz>0 ){
    62      if( fread(pBuf, sz, 1, f)!=1 ){
    63        fprintf(stderr, "cannot read all %d bytes of \"%s\"\n", sz, zFilename);
    64        exit(1);
    65      }
    66      fclose(f);
    67    }
    68    *pSz = sz;
    69    *ppBuf = pBuf;
    70  }
    71  
    72  /* Array for converting from half-bytes (nybbles) into ASCII hex
    73  ** digits. */
    74  static const char hexdigits[] = {
    75    '0', '1', '2', '3', '4', '5', '6', '7',
    76    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 
    77  };
    78  
    79  /*
    80  ** Render an sqlite3_value as an SQL string.
    81  */
    82  static void renderValue(sqlite3_value *pVal){
    83    switch( sqlite3_value_type(pVal) ){
    84      case SQLITE_FLOAT: {
    85        double r1;
    86        char zBuf[50];
    87        r1 = sqlite3_value_double(pVal);
    88        sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
    89        printf("%s", zBuf);
    90        break;
    91      }
    92      case SQLITE_INTEGER: {
    93        printf("%lld", sqlite3_value_int64(pVal));
    94        break;
    95      }
    96      case SQLITE_BLOB: {
    97        char const *zBlob = sqlite3_value_blob(pVal);
    98        int nBlob = sqlite3_value_bytes(pVal);
    99        int i;
   100        printf("x'");
   101        for(i=0; i<nBlob; i++){
   102          putchar(hexdigits[(zBlob[i]>>4)&0x0F]);
   103          putchar(hexdigits[(zBlob[i])&0x0F]);
   104        }
   105        putchar('\'');
   106        break;
   107      }
   108      case SQLITE_TEXT: {
   109        const unsigned char *zArg = sqlite3_value_text(pVal);
   110        putchar('\'');
   111        while( zArg[0] ){
   112          putchar(zArg[0]);
   113          if( zArg[0]=='\'' ) putchar(zArg[0]);
   114          zArg++;
   115        }
   116        putchar('\'');
   117        break;
   118      }
   119      default: {
   120        assert( sqlite3_value_type(pVal)==SQLITE_NULL );
   121        printf("NULL");
   122        break;
   123      }
   124    }
   125  }
   126  
   127  /*
   128  ** Number of conflicts seen
   129  */
   130  static int nConflict = 0;
   131  
   132  /*
   133  ** The conflict callback
   134  */
   135  static int conflictCallback(
   136    void *pCtx,
   137    int eConflict,
   138    sqlite3_changeset_iter *pIter
   139  ){
   140    int op, bIndirect, nCol, i;
   141    const char *zTab;
   142    unsigned char *abPK;
   143    const char *zType = "";
   144    const char *zOp = "";
   145    const char *zSep = " ";
   146  
   147    nConflict++;
   148    sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
   149    sqlite3changeset_pk(pIter, &abPK, 0);
   150    switch( eConflict ){
   151      case SQLITE_CHANGESET_DATA:         zType = "DATA";         break;
   152      case SQLITE_CHANGESET_NOTFOUND:     zType = "NOTFOUND";     break;
   153      case SQLITE_CHANGESET_CONFLICT:     zType = "PRIMARY KEY";  break;
   154      case SQLITE_CHANGESET_FOREIGN_KEY:  zType = "FOREIGN KEY";  break;
   155      case SQLITE_CHANGESET_CONSTRAINT:   zType = "CONSTRAINT";   break;
   156    }
   157    switch( op ){
   158      case SQLITE_UPDATE:     zOp = "UPDATE of";     break;
   159      case SQLITE_INSERT:     zOp = "INSERT into";   break;
   160      case SQLITE_DELETE:     zOp = "DELETE from";   break;
   161    }
   162    printf("%s conflict on %s table %s with primary key", zType, zOp, zTab);
   163    for(i=0; i<nCol; i++){
   164      sqlite3_value *pVal;
   165      if( abPK[i]==0 ) continue;
   166      printf("%s", zSep);
   167      if( op==SQLITE_INSERT ){
   168        sqlite3changeset_new(pIter, i, &pVal);
   169      }else{
   170        sqlite3changeset_old(pIter, i, &pVal);
   171      }
   172      renderValue(pVal);
   173      zSep = ",";
   174    }
   175    printf("\n");
   176    return SQLITE_CHANGESET_OMIT;
   177  }
   178  
   179  int main(int argc, char **argv){
   180    int sz, rc;
   181    void *pBuf = 0;
   182    if( argc<3 ) usage(argv[0]);
   183    readFile(argv[1], &sz, &pBuf);
   184  
   185    /* changeset FILENAME apply DB
   186    ** Apply the changeset in FILENAME to the database file DB
   187    */
   188    if( strcmp(argv[2],"apply")==0 ){
   189      sqlite3 *db;
   190      if( argc!=4 ) usage(argv[0]);
   191      rc = sqlite3_open(argv[3], &db);
   192      if( rc!=SQLITE_OK ){
   193        fprintf(stderr, "unable to open database file \"%s\": %s\n",
   194                argv[3], sqlite3_errmsg(db));
   195        sqlite3_close(db);
   196        exit(1);
   197      }
   198      sqlite3_exec(db, "BEGIN", 0, 0, 0);
   199      nConflict = 0;
   200      rc = sqlite3changeset_apply(db, sz, pBuf, 0, conflictCallback, 0);
   201      if( rc ){
   202        fprintf(stderr, "sqlite3changeset_apply() returned %d\n", rc);
   203      }
   204      if( nConflict ){
   205        fprintf(stderr, "%d conflicts - no changes applied\n", nConflict);
   206        sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
   207      }else if( rc ){
   208        fprintf(stderr, "sqlite3changeset_apply() returns %d "
   209                        "- no changes applied\n", rc);
   210        sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
   211      }else{
   212        sqlite3_exec(db, "COMMIT", 0, 0, 0);
   213      }
   214      sqlite3_close(db);
   215    }else
   216  
   217    /* changeset FILENAME concat FILE2 OUT
   218    ** Add changeset FILE2 onto the end of the changeset in FILENAME
   219    ** and write the result into OUT.
   220    */
   221    if( strcmp(argv[2],"concat")==0 ){
   222      int szB;
   223      void *pB;
   224      int szOut;
   225      void *pOutBuf;
   226      FILE *out;
   227      const char *zOut = argv[4];
   228      if( argc!=5 ) usage(argv[0]);
   229      out = fopen(zOut, "wb");
   230      if( out==0 ){
   231        fprintf(stderr, "cannot open \"%s\" for writing\n", zOut);
   232        exit(1);
   233      }
   234      readFile(argv[3], &szB, &pB);
   235      rc = sqlite3changeset_concat(sz, pBuf, szB, pB, &szOut, &pOutBuf);
   236      if( rc!=SQLITE_OK ){
   237        fprintf(stderr, "sqlite3changeset_concat() returns %d\n", rc);
   238      }else if( szOut>0 && fwrite(pOutBuf, szOut, 1, out)!=1 ){
   239        fprintf(stderr, "unable to write all %d bytes of output to \"%s\"\n",
   240                szOut, zOut);
   241      }
   242      fclose(out);
   243      sqlite3_free(pOutBuf);
   244      sqlite3_free(pB);
   245    }else
   246  
   247    /* changeset FILENAME dump
   248    ** Show the complete content of the changeset in FILENAME
   249    */
   250    if( strcmp(argv[2],"dump")==0 ){
   251      int cnt = 0;
   252      int i;
   253      sqlite3_changeset_iter *pIter;
   254      rc = sqlite3changeset_start(&pIter, sz, pBuf);
   255      if( rc!=SQLITE_OK ){
   256        fprintf(stderr, "sqlite3changeset_start() returns %d\n", rc);
   257        exit(1);
   258      }
   259      while( sqlite3changeset_next(pIter)==SQLITE_ROW ){
   260        int op, bIndirect, nCol;
   261        const char *zTab;
   262        unsigned char *abPK;
   263        sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
   264        cnt++;
   265        printf("%d: %s table=[%s] indirect=%d nColumn=%d\n",
   266               cnt, op==SQLITE_INSERT ? "INSERT" :
   267                         op==SQLITE_UPDATE ? "UPDATE" : "DELETE",
   268               zTab, bIndirect, nCol);
   269        sqlite3changeset_pk(pIter, &abPK, 0);
   270        for(i=0; i<nCol; i++){
   271          sqlite3_value *pVal;
   272          pVal = 0;
   273          sqlite3changeset_old(pIter, i, &pVal);
   274          if( pVal ){
   275            printf("    old[%d]%s = ", i, abPK[i] ? "pk" : "  ");
   276            renderValue(pVal);
   277            printf("\n");
   278          }
   279          pVal = 0;
   280          sqlite3changeset_new(pIter, i, &pVal);
   281          if( pVal ){
   282            printf("    new[%d]%s = ", i, abPK[i] ? "pk" : "  ");
   283            renderValue(pVal);
   284            printf("\n");
   285          }
   286        }
   287      }
   288      sqlite3changeset_finalize(pIter);
   289    }else
   290  
   291    /* changeset FILENAME invert OUT
   292    ** Invert the changes in FILENAME and writes the result on OUT
   293    */
   294    if( strcmp(argv[2],"invert")==0 ){
   295      FILE *out;
   296      int szOut = 0;
   297      void *pOutBuf = 0;
   298      const char *zOut = argv[3];
   299      if( argc!=4 ) usage(argv[0]);
   300      out = fopen(zOut, "wb");
   301      if( out==0 ){
   302        fprintf(stderr, "cannot open \"%s\" for writing\n", zOut);
   303        exit(1);
   304      }
   305      rc = sqlite3changeset_invert(sz, pBuf, &szOut, &pOutBuf);
   306      if( rc!=SQLITE_OK ){
   307        fprintf(stderr, "sqlite3changeset_invert() returns %d\n", rc);
   308      }else if( szOut>0 && fwrite(pOutBuf, szOut, 1, out)!=1 ){
   309        fprintf(stderr, "unable to write all %d bytes of output to \"%s\"\n",
   310                szOut, zOut);
   311      }
   312      fclose(out);
   313      sqlite3_free(pOutBuf);
   314    }else
   315  
   316    /* changeset FILE sql
   317    ** Show the content of the changeset as pseudo-SQL
   318    */
   319    if( strcmp(argv[2],"sql")==0 ){
   320      int cnt = 0;
   321      char *zPrevTab = 0;
   322      char *zSQLTabName = 0;
   323      sqlite3_changeset_iter *pIter = 0;
   324      rc = sqlite3changeset_start(&pIter, sz, pBuf);
   325      if( rc!=SQLITE_OK ){
   326        fprintf(stderr, "sqlite3changeset_start() returns %d\n", rc);
   327        exit(1);
   328      }
   329      printf("BEGIN;\n");
   330      while( sqlite3changeset_next(pIter)==SQLITE_ROW ){
   331        int op, bIndirect, nCol;
   332        const char *zTab;
   333        sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
   334        cnt++;
   335        if( zPrevTab==0 || strcmp(zPrevTab,zTab)!=0 ){
   336          sqlite3_free(zPrevTab);
   337          sqlite3_free(zSQLTabName);
   338          zPrevTab = sqlite3_mprintf("%s", zTab);
   339          if( !isalnum(zTab[0]) || sqlite3_strglob("*[^a-zA-Z0-9]*",zTab)==0 ){
   340            zSQLTabName = sqlite3_mprintf("\"%w\"", zTab);
   341          }else{
   342            zSQLTabName = sqlite3_mprintf("%s", zTab);
   343          }
   344          printf("/****** Changes for table %s ***************/\n", zSQLTabName);
   345        }
   346        switch( op ){
   347          case SQLITE_DELETE: {
   348            unsigned char *abPK;
   349            int i;
   350            const char *zSep = " ";
   351            sqlite3changeset_pk(pIter, &abPK, 0);
   352            printf("/* %d */ DELETE FROM %s WHERE", cnt, zSQLTabName);
   353            for(i=0; i<nCol; i++){
   354              sqlite3_value *pVal;
   355              if( abPK[i]==0 ) continue;
   356              printf("%sc%d=", zSep, i+1);
   357              zSep = " AND ";
   358              sqlite3changeset_old(pIter, i, &pVal);
   359              renderValue(pVal);
   360            }
   361            printf(";\n");
   362            break;
   363          }
   364          case SQLITE_UPDATE: {
   365            unsigned char *abPK;
   366            int i;
   367            const char *zSep = " ";
   368            sqlite3changeset_pk(pIter, &abPK, 0);
   369            printf("/* %d */ UPDATE %s SET", cnt, zSQLTabName);
   370            for(i=0; i<nCol; i++){
   371              sqlite3_value *pVal = 0;
   372              sqlite3changeset_new(pIter, i, &pVal);
   373              if( pVal ){
   374                printf("%sc%d=", zSep, i+1);
   375                zSep = ", ";
   376                renderValue(pVal);
   377              }
   378            }
   379            printf(" WHERE");
   380            zSep = " ";
   381            for(i=0; i<nCol; i++){
   382              sqlite3_value *pVal;
   383              if( abPK[i]==0 ) continue;
   384              printf("%sc%d=", zSep, i+1);
   385              zSep = " AND ";
   386              sqlite3changeset_old(pIter, i, &pVal);
   387              renderValue(pVal);
   388            }
   389            printf(";\n");
   390            break;
   391          }
   392          case SQLITE_INSERT: {
   393            int i;
   394            printf("/* %d */ INSERT INTO %s VALUES", cnt, zSQLTabName);
   395            for(i=0; i<nCol; i++){
   396              sqlite3_value *pVal;
   397              printf("%c", i==0 ? '(' : ',');
   398              sqlite3changeset_new(pIter, i, &pVal);
   399              renderValue(pVal);
   400            }
   401            printf(");\n");
   402            break;
   403          }
   404        }
   405      }
   406      printf("COMMIT;\n");
   407      sqlite3changeset_finalize(pIter);
   408      sqlite3_free(zPrevTab);
   409      sqlite3_free(zSQLTabName);
   410    }else
   411  
   412    /* If nothing else matches, show the usage comment */
   413    usage(argv[0]);
   414    sqlite3_free(pBuf);
   415    return 0; 
   416  }