/ Check-in [d91fcc26]
Login

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

Overview
Comment:Add the delta_parse(DELTA) table-valued function to the fossildelta extension.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: d91fcc267bf1be795dfdb1fbfb40c2aea79ddff247a51d26462136c325b7a6d3
User & Date: drh 2019-02-19 20:19:51
Context
2019-02-19
20:29
Enhance the xBestIndex method on delta_parse() to return SQLITE_CONSTRAINT if no delta argument is supplied. check-in: f16d127c user: drh tags: trunk
20:19
Add the delta_parse(DELTA) table-valued function to the fossildelta extension. check-in: d91fcc26 user: drh tags: trunk
18:39
Add the fossildelta.c extension in ext/misc with implementations of the Fossil delta functions. check-in: b80cafa6 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/misc/fossildelta.c.

   130    130   
   131    131   /*
   132    132   ** Read bytes from *pz and convert them into a positive integer.  When
   133    133   ** finished, leave *pz pointing to the first character past the end of
   134    134   ** the integer.  The *pLen parameter holds the length of the string
   135    135   ** in *pz and is decremented once for each character in the integer.
   136    136   */
   137         -static unsigned int getInt(const char **pz, int *pLen){
          137  +static unsigned int deltaGetInt(const char **pz, int *pLen){
   138    138     static const signed char zValue[] = {
   139    139       -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
   140    140       -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
   141    141       -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
   142    142        0,  1,  2,  3,  4,  5,  6,  7,    8,  9, -1, -1, -1, -1, -1, -1,
   143    143       -1, 10, 11, 12, 13, 14, 15, 16,   17, 18, 19, 20, 21, 22, 23, 24,
   144    144       25, 26, 27, 28, 29, 30, 31, 32,   33, 34, 35, -1, -1, -1, -1, 36,
................................................................................
   480    480   ** This routine is provided so that an procedure that is able
   481    481   ** to call delta_apply() can learn how much space is required
   482    482   ** for the output and hence allocate nor more space that is really
   483    483   ** needed.
   484    484   */
   485    485   static int delta_output_size(const char *zDelta, int lenDelta){
   486    486     int size;
   487         -  size = getInt(&zDelta, &lenDelta);
          487  +  size = deltaGetInt(&zDelta, &lenDelta);
   488    488     if( *zDelta!='\n' ){
   489    489       /* ERROR: size integer not terminated by "\n" */
   490    490       return -1;
   491    491     }
   492    492     return size;
   493    493   }
   494    494   
................................................................................
   522    522   ){
   523    523     unsigned int limit;
   524    524     unsigned int total = 0;
   525    525   #ifdef FOSSIL_ENABLE_DELTA_CKSUM_TEST
   526    526     char *zOrigOut = zOut;
   527    527   #endif
   528    528   
   529         -  limit = getInt(&zDelta, &lenDelta);
          529  +  limit = deltaGetInt(&zDelta, &lenDelta);
   530    530     if( *zDelta!='\n' ){
   531    531       /* ERROR: size integer not terminated by "\n" */
   532    532       return -1;
   533    533     }
   534    534     zDelta++; lenDelta--;
   535    535     while( *zDelta && lenDelta>0 ){
   536    536       unsigned int cnt, ofst;
   537         -    cnt = getInt(&zDelta, &lenDelta);
          537  +    cnt = deltaGetInt(&zDelta, &lenDelta);
   538    538       switch( zDelta[0] ){
   539    539         case '@': {
   540    540           zDelta++; lenDelta--;
   541         -        ofst = getInt(&zDelta, &lenDelta);
          541  +        ofst = deltaGetInt(&zDelta, &lenDelta);
   542    542           if( lenDelta>0 && zDelta[0]!=',' ){
   543    543             /* ERROR: copy command not terminated by ',' */
   544    544             return -1;
   545    545           }
   546    546           zDelta++; lenDelta--;
   547    547           total += cnt;
   548    548           if( total>limit ){
................................................................................
   585    585   #endif
   586    586           if( total!=limit ){
   587    587             /* ERROR: generated size does not match predicted size */
   588    588             return -1;
   589    589           }
   590    590           return total;
   591    591         }
   592         -      default: {
   593         -        /* ERROR: unknown delta operator */
   594         -        return -1;
   595         -      }
   596         -    }
   597         -  }
   598         -  /* ERROR: unterminated delta */
   599         -  return -1;
   600         -}
   601         -
   602         -/*
   603         -** Analyze a delta.  Figure out the total number of bytes copied from
   604         -** source to target, and the total number of bytes inserted by the delta,
   605         -** and return both numbers.
   606         -*/
   607         -static int delta_analyze(
   608         -  const char *zDelta,    /* Delta to apply to the pattern */
   609         -  int lenDelta,          /* Length of the delta */
   610         -  int *pnCopy,           /* OUT: Number of bytes copied */
   611         -  int *pnInsert          /* OUT: Number of bytes inserted */
   612         -){
   613         -  unsigned int nInsert = 0;
   614         -  unsigned int nCopy = 0;
   615         -
   616         -  (void)getInt(&zDelta, &lenDelta);
   617         -  if( *zDelta!='\n' ){
   618         -    /* ERROR: size integer not terminated by "\n" */
   619         -    return -1;
   620         -  }
   621         -  zDelta++; lenDelta--;
   622         -  while( *zDelta && lenDelta>0 ){
   623         -    unsigned int cnt;
   624         -    cnt = getInt(&zDelta, &lenDelta);
   625         -    switch( zDelta[0] ){
   626         -      case '@': {
   627         -        zDelta++; lenDelta--;
   628         -        (void)getInt(&zDelta, &lenDelta);
   629         -        if( lenDelta>0 && zDelta[0]!=',' ){
   630         -          /* ERROR: copy command not terminated by ',' */
   631         -          return -1;
   632         -        }
   633         -        zDelta++; lenDelta--;
   634         -        nCopy += cnt;
   635         -        break;
   636         -      }
   637         -      case ':': {
   638         -        zDelta++; lenDelta--;
   639         -        nInsert += cnt;
   640         -        if( cnt>lenDelta ){
   641         -          /* ERROR: insert count exceeds size of delta */
   642         -          return -1;
   643         -        }
   644         -        zDelta += cnt;
   645         -        lenDelta -= cnt;
   646         -        break;
   647         -      }
   648         -      case ';': {
   649         -        *pnCopy = nCopy;
   650         -        *pnInsert = nInsert;
   651         -        return 0;
   652         -      }
   653    592         default: {
   654    593           /* ERROR: unknown delta operator */
   655    594           return -1;
   656    595         }
   657    596       }
   658    597     }
   659    598     /* ERROR: unterminated delta */
................................................................................
   761    700       sqlite3_result_error(context, "corrupt fossil delta", -1);
   762    701       return;
   763    702     }else{
   764    703       sqlite3_result_int(context, nOut);
   765    704     }
   766    705   }
   767    706   
          707  +/* The deltaparse(DELTA) table-valued function parses the DELTA in
          708  +** its input and returns a table that describes that delta.
          709  +*/
          710  +typedef struct deltaparsevtab_vtab deltaparsevtab_vtab;
          711  +typedef struct deltaparsevtab_cursor deltaparsevtab_cursor;
          712  +struct deltaparsevtab_vtab {
          713  +  sqlite3_vtab base;  /* Base class - must be first */
          714  +  /* No additional information needed */
          715  +};
          716  +struct deltaparsevtab_cursor {
          717  +  sqlite3_vtab_cursor base;  /* Base class - must be first */
          718  +  char *aDelta;              /* The delta being parsed */
          719  +  int nDelta;                /* Number of bytes in the delta */
          720  +  int iCursor;               /* Current cursor location */
          721  +  int eOp;                   /* Name of current operator */
          722  +  unsigned int a1, a2;       /* Arguments to current operator */
          723  +  int iNext;                 /* Next cursor value */
          724  +};
          725  +
          726  +/* Operator names:
          727  +*/
          728  +static const char *azOp[] = {
          729  +  "SIZE", "COPY", "INSERT", "CHECKSUM", "ERROR", "EOF"
          730  +};
          731  +#define DELTAPARSE_OP_SIZE         0
          732  +#define DELTAPARSE_OP_COPY         1
          733  +#define DELTAPARSE_OP_INSERT       2
          734  +#define DELTAPARSE_OP_CHECKSUM     3
          735  +#define DELTAPARSE_OP_ERROR        4
          736  +#define DELTAPARSE_OP_EOF          5
          737  +
          738  +/*
          739  +** The deltaparsevtabConnect() method is invoked to create a new
          740  +** deltaparse virtual table.
          741  +**
          742  +** Think of this routine as the constructor for deltaparsevtab_vtab objects.
          743  +**
          744  +** All this routine needs to do is:
          745  +**
          746  +**    (1) Allocate the deltaparsevtab_vtab object and initialize all fields.
          747  +**
          748  +**    (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
          749  +**        result set of queries against the virtual table will look like.
          750  +*/
          751  +static int deltaparsevtabConnect(
          752  +  sqlite3 *db,
          753  +  void *pAux,
          754  +  int argc, const char *const*argv,
          755  +  sqlite3_vtab **ppVtab,
          756  +  char **pzErr
          757  +){
          758  +  deltaparsevtab_vtab *pNew;
          759  +  int rc;
          760  +
          761  +  rc = sqlite3_declare_vtab(db,
          762  +           "CREATE TABLE x(op,a1,a2,delta HIDDEN)"
          763  +       );
          764  +  /* For convenience, define symbolic names for the index to each column. */
          765  +#define DELTAPARSEVTAB_OP     0
          766  +#define DELTAPARSEVTAB_A1     1
          767  +#define DELTAPARSEVTAB_A2     2
          768  +#define DELTAPARSEVTAB_DELTA  3
          769  +  if( rc==SQLITE_OK ){
          770  +    pNew = sqlite3_malloc64( sizeof(*pNew) );
          771  +    *ppVtab = (sqlite3_vtab*)pNew;
          772  +    if( pNew==0 ) return SQLITE_NOMEM;
          773  +    memset(pNew, 0, sizeof(*pNew));
          774  +  }
          775  +  return rc;
          776  +}
          777  +
          778  +/*
          779  +** This method is the destructor for deltaparsevtab_vtab objects.
          780  +*/
          781  +static int deltaparsevtabDisconnect(sqlite3_vtab *pVtab){
          782  +  deltaparsevtab_vtab *p = (deltaparsevtab_vtab*)pVtab;
          783  +  sqlite3_free(p);
          784  +  return SQLITE_OK;
          785  +}
          786  +
          787  +/*
          788  +** Constructor for a new deltaparsevtab_cursor object.
          789  +*/
          790  +static int deltaparsevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
          791  +  deltaparsevtab_cursor *pCur;
          792  +  pCur = sqlite3_malloc( sizeof(*pCur) );
          793  +  if( pCur==0 ) return SQLITE_NOMEM;
          794  +  memset(pCur, 0, sizeof(*pCur));
          795  +  *ppCursor = &pCur->base;
          796  +  return SQLITE_OK;
          797  +}
          798  +
          799  +/*
          800  +** Destructor for a deltaparsevtab_cursor.
          801  +*/
          802  +static int deltaparsevtabClose(sqlite3_vtab_cursor *cur){
          803  +  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
          804  +  sqlite3_free(pCur);
          805  +  return SQLITE_OK;
          806  +}
          807  +
          808  +
          809  +/*
          810  +** Advance a deltaparsevtab_cursor to its next row of output.
          811  +*/
          812  +static int deltaparsevtabNext(sqlite3_vtab_cursor *cur){
          813  +  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
          814  +  const char *z;
          815  +  int i = 0;
          816  +
          817  +  pCur->iCursor = pCur->iNext;
          818  +  z = pCur->aDelta + pCur->iCursor;
          819  +  pCur->a1 = deltaGetInt(&z, &i);
          820  +  switch( z[0] ){
          821  +    case '@': {
          822  +      z++;
          823  +      pCur->a2 = deltaGetInt(&z, &i);
          824  +      pCur->eOp = DELTAPARSE_OP_COPY;
          825  +      pCur->iNext = (int)(&z[1] - pCur->aDelta);
          826  +      break;
          827  +    }
          828  +    case ':': {
          829  +      z++;
          830  +      pCur->a2 = (unsigned int)(z - pCur->aDelta);
          831  +      pCur->eOp = DELTAPARSE_OP_INSERT;
          832  +      pCur->iNext = (int)(&z[pCur->a1] - pCur->aDelta);
          833  +      break;
          834  +    }
          835  +    case ';': {
          836  +      pCur->eOp = DELTAPARSE_OP_CHECKSUM;
          837  +      pCur->iNext = pCur->nDelta;
          838  +      break;
          839  +    }
          840  +    default: {
          841  +      if( pCur->iNext==pCur->nDelta ){
          842  +        pCur->eOp = DELTAPARSE_OP_EOF;
          843  +      }else{
          844  +        pCur->eOp = DELTAPARSE_OP_ERROR;
          845  +        pCur->iNext = pCur->nDelta;
          846  +      }
          847  +      break;
          848  +    }
          849  +  }
          850  +  return SQLITE_OK;
          851  +}
          852  +
          853  +/*
          854  +** Return values of columns for the row at which the deltaparsevtab_cursor
          855  +** is currently pointing.
          856  +*/
          857  +static int deltaparsevtabColumn(
          858  +  sqlite3_vtab_cursor *cur,   /* The cursor */
          859  +  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
          860  +  int i                       /* Which column to return */
          861  +){
          862  +  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
          863  +  switch( i ){
          864  +    case DELTAPARSEVTAB_OP: {
          865  +      sqlite3_result_text(ctx, azOp[pCur->eOp], -1, SQLITE_STATIC);
          866  +      break;
          867  +    }
          868  +    case DELTAPARSEVTAB_A1: {
          869  +      sqlite3_result_int(ctx, pCur->a1);
          870  +      break;
          871  +    }
          872  +    case DELTAPARSEVTAB_A2: {
          873  +      if( pCur->eOp==DELTAPARSE_OP_COPY ){
          874  +        sqlite3_result_int(ctx, pCur->a2);
          875  +      }else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
          876  +        sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
          877  +                            SQLITE_TRANSIENT);
          878  +      }
          879  +      break;
          880  +    }
          881  +    case DELTAPARSEVTAB_DELTA: {
          882  +      sqlite3_result_blob(ctx, pCur->aDelta, pCur->nDelta, SQLITE_TRANSIENT);
          883  +      break;
          884  +    }
          885  +  }
          886  +  return SQLITE_OK;
          887  +}
          888  +
          889  +/*
          890  +** Return the rowid for the current row.  In this implementation, the
          891  +** rowid is the same as the output value.
          892  +*/
          893  +static int deltaparsevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
          894  +  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
          895  +  *pRowid = pCur->iCursor;
          896  +  return SQLITE_OK;
          897  +}
          898  +
          899  +/*
          900  +** Return TRUE if the cursor has been moved off of the last
          901  +** row of output.
          902  +*/
          903  +static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){
          904  +  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
          905  +  return pCur->eOp==DELTAPARSE_OP_EOF;
          906  +}
          907  +
          908  +/*
          909  +** This method is called to "rewind" the deltaparsevtab_cursor object back
          910  +** to the first row of output.  This method is always called at least
          911  +** once prior to any call to deltaparsevtabColumn() or deltaparsevtabRowid() or 
          912  +** deltaparsevtabEof().
          913  +*/
          914  +static int deltaparsevtabFilter(
          915  +  sqlite3_vtab_cursor *pVtabCursor, 
          916  +  int idxNum, const char *idxStr,
          917  +  int argc, sqlite3_value **argv
          918  +){
          919  +  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor *)pVtabCursor;
          920  +  const char *a;
          921  +  int i = 0;
          922  +  pCur->eOp = DELTAPARSE_OP_ERROR;
          923  +  if( idxNum!=1 ){
          924  +    return SQLITE_OK;
          925  +  }
          926  +  pCur->nDelta = sqlite3_value_bytes(argv[0]);
          927  +  a = (const char*)sqlite3_value_blob(argv[0]);
          928  +  if( pCur->nDelta==0 || a==0 ){
          929  +    return SQLITE_OK;
          930  +  }
          931  +  pCur->aDelta = sqlite3_malloc64( pCur->nDelta+1 );
          932  +  if( pCur->aDelta==0 ){
          933  +    pCur->nDelta = 0;
          934  +    return SQLITE_NOMEM;
          935  +  }
          936  +  memcpy(pCur->aDelta, a, pCur->nDelta);
          937  +  pCur->aDelta[pCur->nDelta] = 0;
          938  +  a = pCur->aDelta;
          939  +  pCur->eOp = DELTAPARSE_OP_SIZE;
          940  +  pCur->a1 = deltaGetInt(&a, &i);
          941  +  if( a[0]!='\n' ){
          942  +    pCur->eOp = DELTAPARSE_OP_ERROR;
          943  +    pCur->a1 = pCur->a2 = 0;
          944  +    pCur->iNext = pCur->nDelta;
          945  +    return SQLITE_OK;
          946  +  }
          947  +  a++;
          948  +  pCur->iNext = (unsigned int)(a - pCur->aDelta);
          949  +  return SQLITE_OK;
          950  +}
          951  +
          952  +/*
          953  +** SQLite will invoke this method one or more times while planning a query
          954  +** that uses the virtual table.  This routine needs to create
          955  +** a query plan for each invocation and compute an estimated cost for that
          956  +** plan.
          957  +*/
          958  +static int deltaparsevtabBestIndex(
          959  +  sqlite3_vtab *tab,
          960  +  sqlite3_index_info *pIdxInfo
          961  +){
          962  +  int i;
          963  +  for(i=0; i<pIdxInfo->nConstraint; i++){
          964  +    if( pIdxInfo->aConstraint[i].iColumn != DELTAPARSEVTAB_DELTA ) continue;
          965  +    if( pIdxInfo->aConstraint[i].usable==0 ) continue;
          966  +    if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
          967  +    pIdxInfo->aConstraintUsage[i].argvIndex = 1;
          968  +    pIdxInfo->aConstraintUsage[i].omit = 1;
          969  +    pIdxInfo->estimatedCost = (double)1;
          970  +    pIdxInfo->estimatedRows = 10;
          971  +    pIdxInfo->idxNum = 1;
          972  +    return SQLITE_OK;
          973  +  }
          974  +  pIdxInfo->idxNum = 0;
          975  +  pIdxInfo->estimatedCost = (double)0x7fffffff;
          976  +  pIdxInfo->estimatedRows = 0x7fffffff;
          977  +  return SQLITE_OK;
          978  +}
          979  +
          980  +/*
          981  +** This following structure defines all the methods for the 
          982  +** virtual table.
          983  +*/
          984  +static sqlite3_module deltaparsevtabModule = {
          985  +  /* iVersion    */ 0,
          986  +  /* xCreate     */ 0,
          987  +  /* xConnect    */ deltaparsevtabConnect,
          988  +  /* xBestIndex  */ deltaparsevtabBestIndex,
          989  +  /* xDisconnect */ deltaparsevtabDisconnect,
          990  +  /* xDestroy    */ 0,
          991  +  /* xOpen       */ deltaparsevtabOpen,
          992  +  /* xClose      */ deltaparsevtabClose,
          993  +  /* xFilter     */ deltaparsevtabFilter,
          994  +  /* xNext       */ deltaparsevtabNext,
          995  +  /* xEof        */ deltaparsevtabEof,
          996  +  /* xColumn     */ deltaparsevtabColumn,
          997  +  /* xRowid      */ deltaparsevtabRowid,
          998  +  /* xUpdate     */ 0,
          999  +  /* xBegin      */ 0,
         1000  +  /* xSync       */ 0,
         1001  +  /* xCommit     */ 0,
         1002  +  /* xRollback   */ 0,
         1003  +  /* xFindMethod */ 0,
         1004  +  /* xRename     */ 0,
         1005  +  /* xSavepoint  */ 0,
         1006  +  /* xRelease    */ 0,
         1007  +  /* xRollbackTo */ 0,
         1008  +  /* xShadowName */ 0
         1009  +};
         1010  +
         1011  +
   768   1012   
   769   1013   #ifdef _WIN32
   770   1014   __declspec(dllexport)
   771   1015   #endif
   772   1016   int sqlite3_fossildelta_init(
   773   1017     sqlite3 *db, 
   774   1018     char **pzErrMsg, 
................................................................................
   783   1027       rc = sqlite3_create_function(db, "delta_apply", 2, SQLITE_UTF8, 0,
   784   1028                                    deltaApplyFunc, 0, 0);
   785   1029     }
   786   1030     if( rc==SQLITE_OK ){
   787   1031       rc = sqlite3_create_function(db, "delta_output_size", 1, SQLITE_UTF8, 0,
   788   1032                                    deltaOutputSizeFunc, 0, 0);
   789   1033     }
         1034  +  if( rc==SQLITE_OK ){
         1035  +    rc = sqlite3_create_module(db, "delta_parse", &deltaparsevtabModule, 0);
         1036  +  }
   790   1037     return rc;
   791   1038   }