| Index: third_party/sqlite/src/ext/session/changeset.c
 | 
| diff --git a/third_party/sqlite/src/ext/session/changeset.c b/third_party/sqlite/src/ext/session/changeset.c
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..edb43ff8e52d601a70327bedea746b02ad7627ad
 | 
| --- /dev/null
 | 
| +++ b/third_party/sqlite/src/ext/session/changeset.c
 | 
| @@ -0,0 +1,416 @@
 | 
| +/*
 | 
| +** 2014-08-18
 | 
| +**
 | 
| +** The author disclaims copyright to this source code.  In place of
 | 
| +** a legal notice, here is a blessing:
 | 
| +**
 | 
| +**    May you do good and not evil.
 | 
| +**    May you find forgiveness for yourself and forgive others.
 | 
| +**    May you share freely, never taking more than you give.
 | 
| +**
 | 
| +*************************************************************************
 | 
| +** This file contains code to implement the "changeset" command line
 | 
| +** utility for displaying and transforming changesets generated by
 | 
| +** the Sessions extension.
 | 
| +*/
 | 
| +#include "sqlite3.h"
 | 
| +#include <stdio.h>
 | 
| +#include <stdlib.h>
 | 
| +#include <string.h>
 | 
| +#include <assert.h>
 | 
| +#include <ctype.h>
 | 
| +
 | 
| +
 | 
| +/*
 | 
| +** Show a usage message on stderr then quit.
 | 
| +*/
 | 
| +static void usage(const char *argv0){
 | 
| +  fprintf(stderr, "Usage: %s FILENAME COMMAND ...\n", argv0);
 | 
| +  fprintf(stderr,
 | 
| +    "COMMANDs:\n"
 | 
| +    "   apply DB           Apply the changeset to database file DB\n"
 | 
| +    "   concat FILE2 OUT   Concatenate FILENAME and FILE2 into OUT\n"
 | 
| +    "   dump               Show the complete content of the changeset\n"
 | 
| +    "   invert OUT         Write an inverted changeset into file OUT\n"
 | 
| +    "   sql                Give a pseudo-SQL rendering of the changeset\n"
 | 
| +  );
 | 
| +  exit(1);
 | 
| +}
 | 
| +
 | 
| +/*
 | 
| +** Read the content of a disk file into an in-memory buffer
 | 
| +*/
 | 
| +static void readFile(const char *zFilename, int *pSz, void **ppBuf){
 | 
| +  FILE *f;
 | 
| +  int sz;
 | 
| +  void *pBuf;
 | 
| +  f = fopen(zFilename, "rb");
 | 
| +  if( f==0 ){
 | 
| +    fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
 | 
| +    exit(1);
 | 
| +  }
 | 
| +  fseek(f, 0, SEEK_END);
 | 
| +  sz = (int)ftell(f);
 | 
| +  rewind(f);
 | 
| +  pBuf = sqlite3_malloc( sz ? sz : 1 );
 | 
| +  if( pBuf==0 ){
 | 
| +    fprintf(stderr, "cannot allocate %d to hold content of \"%s\"\n",
 | 
| +            sz, zFilename);
 | 
| +    exit(1);
 | 
| +  }
 | 
| +  if( sz>0 ){
 | 
| +    if( fread(pBuf, sz, 1, f)!=1 ){
 | 
| +      fprintf(stderr, "cannot read all %d bytes of \"%s\"\n", sz, zFilename);
 | 
| +      exit(1);
 | 
| +    }
 | 
| +    fclose(f);
 | 
| +  }
 | 
| +  *pSz = sz;
 | 
| +  *ppBuf = pBuf;
 | 
| +}
 | 
| +
 | 
| +/* Array for converting from half-bytes (nybbles) into ASCII hex
 | 
| +** digits. */
 | 
| +static const char hexdigits[] = {
 | 
| +  '0', '1', '2', '3', '4', '5', '6', '7',
 | 
| +  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 
 | 
| +};
 | 
| +
 | 
| +/*
 | 
| +** Render an sqlite3_value as an SQL string.
 | 
| +*/
 | 
| +static void renderValue(sqlite3_value *pVal){
 | 
| +  switch( sqlite3_value_type(pVal) ){
 | 
| +    case SQLITE_FLOAT: {
 | 
| +      double r1;
 | 
| +      char zBuf[50];
 | 
| +      r1 = sqlite3_value_double(pVal);
 | 
| +      sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
 | 
| +      printf("%s", zBuf);
 | 
| +      break;
 | 
| +    }
 | 
| +    case SQLITE_INTEGER: {
 | 
| +      printf("%lld", sqlite3_value_int64(pVal));
 | 
| +      break;
 | 
| +    }
 | 
| +    case SQLITE_BLOB: {
 | 
| +      char const *zBlob = sqlite3_value_blob(pVal);
 | 
| +      int nBlob = sqlite3_value_bytes(pVal);
 | 
| +      int i;
 | 
| +      printf("x'");
 | 
| +      for(i=0; i<nBlob; i++){
 | 
| +        putchar(hexdigits[(zBlob[i]>>4)&0x0F]);
 | 
| +        putchar(hexdigits[(zBlob[i])&0x0F]);
 | 
| +      }
 | 
| +      putchar('\'');
 | 
| +      break;
 | 
| +    }
 | 
| +    case SQLITE_TEXT: {
 | 
| +      const unsigned char *zArg = sqlite3_value_text(pVal);
 | 
| +      putchar('\'');
 | 
| +      while( zArg[0] ){
 | 
| +        putchar(zArg[0]);
 | 
| +        if( zArg[0]=='\'' ) putchar(zArg[0]);
 | 
| +        zArg++;
 | 
| +      }
 | 
| +      putchar('\'');
 | 
| +      break;
 | 
| +    }
 | 
| +    default: {
 | 
| +      assert( sqlite3_value_type(pVal)==SQLITE_NULL );
 | 
| +      printf("NULL");
 | 
| +      break;
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +/*
 | 
| +** Number of conflicts seen
 | 
| +*/
 | 
| +static int nConflict = 0;
 | 
| +
 | 
| +/*
 | 
| +** The conflict callback
 | 
| +*/
 | 
| +static int conflictCallback(
 | 
| +  void *pCtx,
 | 
| +  int eConflict,
 | 
| +  sqlite3_changeset_iter *pIter
 | 
| +){
 | 
| +  int op, bIndirect, nCol, i;
 | 
| +  const char *zTab;
 | 
| +  unsigned char *abPK;
 | 
| +  const char *zType = "";
 | 
| +  const char *zOp = "";
 | 
| +  const char *zSep = " ";
 | 
| +
 | 
| +  nConflict++;
 | 
| +  sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
 | 
| +  sqlite3changeset_pk(pIter, &abPK, 0);
 | 
| +  switch( eConflict ){
 | 
| +    case SQLITE_CHANGESET_DATA:         zType = "DATA";         break;
 | 
| +    case SQLITE_CHANGESET_NOTFOUND:     zType = "NOTFOUND";     break;
 | 
| +    case SQLITE_CHANGESET_CONFLICT:     zType = "PRIMARY KEY";  break;
 | 
| +    case SQLITE_CHANGESET_FOREIGN_KEY:  zType = "FOREIGN KEY";  break;
 | 
| +    case SQLITE_CHANGESET_CONSTRAINT:   zType = "CONSTRAINT";   break;
 | 
| +  }
 | 
| +  switch( op ){
 | 
| +    case SQLITE_UPDATE:     zOp = "UPDATE of";     break;
 | 
| +    case SQLITE_INSERT:     zOp = "INSERT into";   break;
 | 
| +    case SQLITE_DELETE:     zOp = "DELETE from";   break;
 | 
| +  }
 | 
| +  printf("%s conflict on %s table %s with primary key", zType, zOp, zTab);
 | 
| +  for(i=0; i<nCol; i++){
 | 
| +    sqlite3_value *pVal;
 | 
| +    if( abPK[i]==0 ) continue;
 | 
| +    printf("%s", zSep);
 | 
| +    if( op==SQLITE_INSERT ){
 | 
| +      sqlite3changeset_new(pIter, i, &pVal);
 | 
| +    }else{
 | 
| +      sqlite3changeset_old(pIter, i, &pVal);
 | 
| +    }
 | 
| +    renderValue(pVal);
 | 
| +    zSep = ",";
 | 
| +  }
 | 
| +  printf("\n");
 | 
| +  return SQLITE_CHANGESET_OMIT;
 | 
| +}
 | 
| +
 | 
| +int main(int argc, char **argv){
 | 
| +  int sz, rc;
 | 
| +  void *pBuf = 0;
 | 
| +  if( argc<3 ) usage(argv[0]);
 | 
| +  readFile(argv[1], &sz, &pBuf);
 | 
| +
 | 
| +  /* changeset FILENAME apply DB
 | 
| +  ** Apply the changeset in FILENAME to the database file DB
 | 
| +  */
 | 
| +  if( strcmp(argv[2],"apply")==0 ){
 | 
| +    sqlite3 *db;
 | 
| +    if( argc!=4 ) usage(argv[0]);
 | 
| +    rc = sqlite3_open(argv[3], &db);
 | 
| +    if( rc!=SQLITE_OK ){
 | 
| +      fprintf(stderr, "unable to open database file \"%s\": %s\n",
 | 
| +              argv[3], sqlite3_errmsg(db));
 | 
| +      sqlite3_close(db);
 | 
| +      exit(1);
 | 
| +    }
 | 
| +    sqlite3_exec(db, "BEGIN", 0, 0, 0);
 | 
| +    nConflict = 0;
 | 
| +    rc = sqlite3changeset_apply(db, sz, pBuf, 0, conflictCallback, 0);
 | 
| +    if( rc ){
 | 
| +      fprintf(stderr, "sqlite3changeset_apply() returned %d\n", rc);
 | 
| +    }
 | 
| +    if( nConflict ){
 | 
| +      fprintf(stderr, "%d conflicts - no changes applied\n", nConflict);
 | 
| +      sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
 | 
| +    }else if( rc ){
 | 
| +      fprintf(stderr, "sqlite3changeset_apply() returns %d "
 | 
| +                      "- no changes applied\n", rc);
 | 
| +      sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
 | 
| +    }else{
 | 
| +      sqlite3_exec(db, "COMMIT", 0, 0, 0);
 | 
| +    }
 | 
| +    sqlite3_close(db);
 | 
| +  }else
 | 
| +
 | 
| +  /* changeset FILENAME concat FILE2 OUT
 | 
| +  ** Add changeset FILE2 onto the end of the changeset in FILENAME
 | 
| +  ** and write the result into OUT.
 | 
| +  */
 | 
| +  if( strcmp(argv[2],"concat")==0 ){
 | 
| +    int szB;
 | 
| +    void *pB;
 | 
| +    int szOut;
 | 
| +    void *pOutBuf;
 | 
| +    FILE *out;
 | 
| +    const char *zOut = argv[4];
 | 
| +    if( argc!=5 ) usage(argv[0]);
 | 
| +    out = fopen(zOut, "wb");
 | 
| +    if( out==0 ){
 | 
| +      fprintf(stderr, "cannot open \"%s\" for writing\n", zOut);
 | 
| +      exit(1);
 | 
| +    }
 | 
| +    readFile(argv[3], &szB, &pB);
 | 
| +    rc = sqlite3changeset_concat(sz, pBuf, szB, pB, &szOut, &pOutBuf);
 | 
| +    if( rc!=SQLITE_OK ){
 | 
| +      fprintf(stderr, "sqlite3changeset_concat() returns %d\n", rc);
 | 
| +    }else if( szOut>0 && fwrite(pOutBuf, szOut, 1, out)!=1 ){
 | 
| +      fprintf(stderr, "unable to write all %d bytes of output to \"%s\"\n",
 | 
| +              szOut, zOut);
 | 
| +    }
 | 
| +    fclose(out);
 | 
| +    sqlite3_free(pOutBuf);
 | 
| +    sqlite3_free(pB);
 | 
| +  }else
 | 
| +
 | 
| +  /* changeset FILENAME dump
 | 
| +  ** Show the complete content of the changeset in FILENAME
 | 
| +  */
 | 
| +  if( strcmp(argv[2],"dump")==0 ){
 | 
| +    int cnt = 0;
 | 
| +    int i;
 | 
| +    sqlite3_changeset_iter *pIter;
 | 
| +    rc = sqlite3changeset_start(&pIter, sz, pBuf);
 | 
| +    if( rc!=SQLITE_OK ){
 | 
| +      fprintf(stderr, "sqlite3changeset_start() returns %d\n", rc);
 | 
| +      exit(1);
 | 
| +    }
 | 
| +    while( sqlite3changeset_next(pIter)==SQLITE_ROW ){
 | 
| +      int op, bIndirect, nCol;
 | 
| +      const char *zTab;
 | 
| +      unsigned char *abPK;
 | 
| +      sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
 | 
| +      cnt++;
 | 
| +      printf("%d: %s table=[%s] indirect=%d nColumn=%d\n",
 | 
| +             cnt, op==SQLITE_INSERT ? "INSERT" :
 | 
| +                       op==SQLITE_UPDATE ? "UPDATE" : "DELETE",
 | 
| +             zTab, bIndirect, nCol);
 | 
| +      sqlite3changeset_pk(pIter, &abPK, 0);
 | 
| +      for(i=0; i<nCol; i++){
 | 
| +        sqlite3_value *pVal;
 | 
| +        pVal = 0;
 | 
| +        sqlite3changeset_old(pIter, i, &pVal);
 | 
| +        if( pVal ){
 | 
| +          printf("    old[%d]%s = ", i, abPK[i] ? "pk" : "  ");
 | 
| +          renderValue(pVal);
 | 
| +          printf("\n");
 | 
| +        }
 | 
| +        pVal = 0;
 | 
| +        sqlite3changeset_new(pIter, i, &pVal);
 | 
| +        if( pVal ){
 | 
| +          printf("    new[%d]%s = ", i, abPK[i] ? "pk" : "  ");
 | 
| +          renderValue(pVal);
 | 
| +          printf("\n");
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +    sqlite3changeset_finalize(pIter);
 | 
| +  }else
 | 
| +
 | 
| +  /* changeset FILENAME invert OUT
 | 
| +  ** Invert the changes in FILENAME and writes the result on OUT
 | 
| +  */
 | 
| +  if( strcmp(argv[2],"invert")==0 ){
 | 
| +    FILE *out;
 | 
| +    int szOut = 0;
 | 
| +    void *pOutBuf = 0;
 | 
| +    const char *zOut = argv[3];
 | 
| +    if( argc!=4 ) usage(argv[0]);
 | 
| +    out = fopen(zOut, "wb");
 | 
| +    if( out==0 ){
 | 
| +      fprintf(stderr, "cannot open \"%s\" for writing\n", zOut);
 | 
| +      exit(1);
 | 
| +    }
 | 
| +    rc = sqlite3changeset_invert(sz, pBuf, &szOut, &pOutBuf);
 | 
| +    if( rc!=SQLITE_OK ){
 | 
| +      fprintf(stderr, "sqlite3changeset_invert() returns %d\n", rc);
 | 
| +    }else if( szOut>0 && fwrite(pOutBuf, szOut, 1, out)!=1 ){
 | 
| +      fprintf(stderr, "unable to write all %d bytes of output to \"%s\"\n",
 | 
| +              szOut, zOut);
 | 
| +    }
 | 
| +    fclose(out);
 | 
| +    sqlite3_free(pOutBuf);
 | 
| +  }else
 | 
| +
 | 
| +  /* changeset FILE sql
 | 
| +  ** Show the content of the changeset as pseudo-SQL
 | 
| +  */
 | 
| +  if( strcmp(argv[2],"sql")==0 ){
 | 
| +    int cnt = 0;
 | 
| +    char *zPrevTab = 0;
 | 
| +    char *zSQLTabName = 0;
 | 
| +    sqlite3_changeset_iter *pIter = 0;
 | 
| +    rc = sqlite3changeset_start(&pIter, sz, pBuf);
 | 
| +    if( rc!=SQLITE_OK ){
 | 
| +      fprintf(stderr, "sqlite3changeset_start() returns %d\n", rc);
 | 
| +      exit(1);
 | 
| +    }
 | 
| +    printf("BEGIN;\n");
 | 
| +    while( sqlite3changeset_next(pIter)==SQLITE_ROW ){
 | 
| +      int op, bIndirect, nCol;
 | 
| +      const char *zTab;
 | 
| +      sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
 | 
| +      cnt++;
 | 
| +      if( zPrevTab==0 || strcmp(zPrevTab,zTab)!=0 ){
 | 
| +        sqlite3_free(zPrevTab);
 | 
| +        sqlite3_free(zSQLTabName);
 | 
| +        zPrevTab = sqlite3_mprintf("%s", zTab);
 | 
| +        if( !isalnum(zTab[0]) || sqlite3_strglob("*[^a-zA-Z0-9]*",zTab)==0 ){
 | 
| +          zSQLTabName = sqlite3_mprintf("\"%w\"", zTab);
 | 
| +        }else{
 | 
| +          zSQLTabName = sqlite3_mprintf("%s", zTab);
 | 
| +        }
 | 
| +        printf("/****** Changes for table %s ***************/\n", zSQLTabName);
 | 
| +      }
 | 
| +      switch( op ){
 | 
| +        case SQLITE_DELETE: {
 | 
| +          unsigned char *abPK;
 | 
| +          int i;
 | 
| +          const char *zSep = " ";
 | 
| +          sqlite3changeset_pk(pIter, &abPK, 0);
 | 
| +          printf("/* %d */ DELETE FROM %s WHERE", cnt, zSQLTabName);
 | 
| +          for(i=0; i<nCol; i++){
 | 
| +            sqlite3_value *pVal;
 | 
| +            if( abPK[i]==0 ) continue;
 | 
| +            printf("%sc%d=", zSep, i+1);
 | 
| +            zSep = " AND ";
 | 
| +            sqlite3changeset_old(pIter, i, &pVal);
 | 
| +            renderValue(pVal);
 | 
| +          }
 | 
| +          printf(";\n");
 | 
| +          break;
 | 
| +        }
 | 
| +        case SQLITE_UPDATE: {
 | 
| +          unsigned char *abPK;
 | 
| +          int i;
 | 
| +          const char *zSep = " ";
 | 
| +          sqlite3changeset_pk(pIter, &abPK, 0);
 | 
| +          printf("/* %d */ UPDATE %s SET", cnt, zSQLTabName);
 | 
| +          for(i=0; i<nCol; i++){
 | 
| +            sqlite3_value *pVal = 0;
 | 
| +            sqlite3changeset_new(pIter, i, &pVal);
 | 
| +            if( pVal ){
 | 
| +              printf("%sc%d=", zSep, i+1);
 | 
| +              zSep = ", ";
 | 
| +              renderValue(pVal);
 | 
| +            }
 | 
| +          }
 | 
| +          printf(" WHERE");
 | 
| +          zSep = " ";
 | 
| +          for(i=0; i<nCol; i++){
 | 
| +            sqlite3_value *pVal;
 | 
| +            if( abPK[i]==0 ) continue;
 | 
| +            printf("%sc%d=", zSep, i+1);
 | 
| +            zSep = " AND ";
 | 
| +            sqlite3changeset_old(pIter, i, &pVal);
 | 
| +            renderValue(pVal);
 | 
| +          }
 | 
| +          printf(";\n");
 | 
| +          break;
 | 
| +        }
 | 
| +        case SQLITE_INSERT: {
 | 
| +          int i;
 | 
| +          printf("/* %d */ INSERT INTO %s VALUES", cnt, zSQLTabName);
 | 
| +          for(i=0; i<nCol; i++){
 | 
| +            sqlite3_value *pVal;
 | 
| +            printf("%c", i==0 ? '(' : ',');
 | 
| +            sqlite3changeset_new(pIter, i, &pVal);
 | 
| +            renderValue(pVal);
 | 
| +          }
 | 
| +          printf(");\n");
 | 
| +          break;
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +    printf("COMMIT;\n");
 | 
| +    sqlite3changeset_finalize(pIter);
 | 
| +    sqlite3_free(zPrevTab);
 | 
| +    sqlite3_free(zSQLTabName);
 | 
| +  }else
 | 
| +
 | 
| +  /* If nothing else matches, show the usage comment */
 | 
| +  usage(argv[0]);
 | 
| +  sqlite3_free(pBuf);
 | 
| +  return 0; 
 | 
| +}
 | 
| 
 |