]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the delta_parse(DELTA) table-valued function to the fossildelta
authordrh <drh@noemail.net>
Tue, 19 Feb 2019 20:19:51 +0000 (20:19 +0000)
committerdrh <drh@noemail.net>
Tue, 19 Feb 2019 20:19:51 +0000 (20:19 +0000)
extension.

FossilOrigin-Name: d91fcc267bf1be795dfdb1fbfb40c2aea79ddff247a51d26462136c325b7a6d3

ext/misc/fossildelta.c
manifest
manifest.uuid

index 3d845faa9814dda855361367193f00584be8ed45..d1b5a2c6a051a262d8eb11ffd1a02ad141bf2dd0 100644 (file)
@@ -134,7 +134,7 @@ static void putInt(unsigned int v, char **pz){
 ** the integer.  The *pLen parameter holds the length of the string
 ** in *pz and is decremented once for each character in the integer.
 */
-static unsigned int getInt(const char **pz, int *pLen){
+static unsigned int deltaGetInt(const char **pz, int *pLen){
   static const signed char zValue[] = {
     -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
     -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
@@ -484,7 +484,7 @@ static int delta_create(
 */
 static int delta_output_size(const char *zDelta, int lenDelta){
   int size;
-  size = getInt(&zDelta, &lenDelta);
+  size = deltaGetInt(&zDelta, &lenDelta);
   if( *zDelta!='\n' ){
     /* ERROR: size integer not terminated by "\n" */
     return -1;
@@ -526,7 +526,7 @@ static int delta_apply(
   char *zOrigOut = zOut;
 #endif
 
-  limit = getInt(&zDelta, &lenDelta);
+  limit = deltaGetInt(&zDelta, &lenDelta);
   if( *zDelta!='\n' ){
     /* ERROR: size integer not terminated by "\n" */
     return -1;
@@ -534,11 +534,11 @@ static int delta_apply(
   zDelta++; lenDelta--;
   while( *zDelta && lenDelta>0 ){
     unsigned int cnt, ofst;
-    cnt = getInt(&zDelta, &lenDelta);
+    cnt = deltaGetInt(&zDelta, &lenDelta);
     switch( zDelta[0] ){
       case '@': {
         zDelta++; lenDelta--;
-        ofst = getInt(&zDelta, &lenDelta);
+        ofst = deltaGetInt(&zDelta, &lenDelta);
         if( lenDelta>0 && zDelta[0]!=',' ){
           /* ERROR: copy command not terminated by ',' */
           return -1;
@@ -599,67 +599,6 @@ static int delta_apply(
   return -1;
 }
 
-/*
-** Analyze a delta.  Figure out the total number of bytes copied from
-** source to target, and the total number of bytes inserted by the delta,
-** and return both numbers.
-*/
-static int delta_analyze(
-  const char *zDelta,    /* Delta to apply to the pattern */
-  int lenDelta,          /* Length of the delta */
-  int *pnCopy,           /* OUT: Number of bytes copied */
-  int *pnInsert          /* OUT: Number of bytes inserted */
-){
-  unsigned int nInsert = 0;
-  unsigned int nCopy = 0;
-
-  (void)getInt(&zDelta, &lenDelta);
-  if( *zDelta!='\n' ){
-    /* ERROR: size integer not terminated by "\n" */
-    return -1;
-  }
-  zDelta++; lenDelta--;
-  while( *zDelta && lenDelta>0 ){
-    unsigned int cnt;
-    cnt = getInt(&zDelta, &lenDelta);
-    switch( zDelta[0] ){
-      case '@': {
-        zDelta++; lenDelta--;
-        (void)getInt(&zDelta, &lenDelta);
-        if( lenDelta>0 && zDelta[0]!=',' ){
-          /* ERROR: copy command not terminated by ',' */
-          return -1;
-        }
-        zDelta++; lenDelta--;
-        nCopy += cnt;
-        break;
-      }
-      case ':': {
-        zDelta++; lenDelta--;
-        nInsert += cnt;
-        if( cnt>lenDelta ){
-          /* ERROR: insert count exceeds size of delta */
-          return -1;
-        }
-        zDelta += cnt;
-        lenDelta -= cnt;
-        break;
-      }
-      case ';': {
-        *pnCopy = nCopy;
-        *pnInsert = nInsert;
-        return 0;
-      }
-      default: {
-        /* ERROR: unknown delta operator */
-        return -1;
-      }
-    }
-  }
-  /* ERROR: unterminated delta */
-  return -1;
-}
-
 /*
 ** SQL functions:  fossildelta_create(X,Y)
 **
@@ -765,6 +704,311 @@ static void deltaOutputSizeFunc(
   }
 }
 
+/* The deltaparse(DELTA) table-valued function parses the DELTA in
+** its input and returns a table that describes that delta.
+*/
+typedef struct deltaparsevtab_vtab deltaparsevtab_vtab;
+typedef struct deltaparsevtab_cursor deltaparsevtab_cursor;
+struct deltaparsevtab_vtab {
+  sqlite3_vtab base;  /* Base class - must be first */
+  /* No additional information needed */
+};
+struct deltaparsevtab_cursor {
+  sqlite3_vtab_cursor base;  /* Base class - must be first */
+  char *aDelta;              /* The delta being parsed */
+  int nDelta;                /* Number of bytes in the delta */
+  int iCursor;               /* Current cursor location */
+  int eOp;                   /* Name of current operator */
+  unsigned int a1, a2;       /* Arguments to current operator */
+  int iNext;                 /* Next cursor value */
+};
+
+/* Operator names:
+*/
+static const char *azOp[] = {
+  "SIZE", "COPY", "INSERT", "CHECKSUM", "ERROR", "EOF"
+};
+#define DELTAPARSE_OP_SIZE         0
+#define DELTAPARSE_OP_COPY         1
+#define DELTAPARSE_OP_INSERT       2
+#define DELTAPARSE_OP_CHECKSUM     3
+#define DELTAPARSE_OP_ERROR        4
+#define DELTAPARSE_OP_EOF          5
+
+/*
+** The deltaparsevtabConnect() method is invoked to create a new
+** deltaparse virtual table.
+**
+** Think of this routine as the constructor for deltaparsevtab_vtab objects.
+**
+** All this routine needs to do is:
+**
+**    (1) Allocate the deltaparsevtab_vtab object and initialize all fields.
+**
+**    (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
+**        result set of queries against the virtual table will look like.
+*/
+static int deltaparsevtabConnect(
+  sqlite3 *db,
+  void *pAux,
+  int argc, const char *const*argv,
+  sqlite3_vtab **ppVtab,
+  char **pzErr
+){
+  deltaparsevtab_vtab *pNew;
+  int rc;
+
+  rc = sqlite3_declare_vtab(db,
+           "CREATE TABLE x(op,a1,a2,delta HIDDEN)"
+       );
+  /* For convenience, define symbolic names for the index to each column. */
+#define DELTAPARSEVTAB_OP     0
+#define DELTAPARSEVTAB_A1     1
+#define DELTAPARSEVTAB_A2     2
+#define DELTAPARSEVTAB_DELTA  3
+  if( rc==SQLITE_OK ){
+    pNew = sqlite3_malloc64( sizeof(*pNew) );
+    *ppVtab = (sqlite3_vtab*)pNew;
+    if( pNew==0 ) return SQLITE_NOMEM;
+    memset(pNew, 0, sizeof(*pNew));
+  }
+  return rc;
+}
+
+/*
+** This method is the destructor for deltaparsevtab_vtab objects.
+*/
+static int deltaparsevtabDisconnect(sqlite3_vtab *pVtab){
+  deltaparsevtab_vtab *p = (deltaparsevtab_vtab*)pVtab;
+  sqlite3_free(p);
+  return SQLITE_OK;
+}
+
+/*
+** Constructor for a new deltaparsevtab_cursor object.
+*/
+static int deltaparsevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+  deltaparsevtab_cursor *pCur;
+  pCur = sqlite3_malloc( sizeof(*pCur) );
+  if( pCur==0 ) return SQLITE_NOMEM;
+  memset(pCur, 0, sizeof(*pCur));
+  *ppCursor = &pCur->base;
+  return SQLITE_OK;
+}
+
+/*
+** Destructor for a deltaparsevtab_cursor.
+*/
+static int deltaparsevtabClose(sqlite3_vtab_cursor *cur){
+  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
+  sqlite3_free(pCur);
+  return SQLITE_OK;
+}
+
+
+/*
+** Advance a deltaparsevtab_cursor to its next row of output.
+*/
+static int deltaparsevtabNext(sqlite3_vtab_cursor *cur){
+  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
+  const char *z;
+  int i = 0;
+
+  pCur->iCursor = pCur->iNext;
+  z = pCur->aDelta + pCur->iCursor;
+  pCur->a1 = deltaGetInt(&z, &i);
+  switch( z[0] ){
+    case '@': {
+      z++;
+      pCur->a2 = deltaGetInt(&z, &i);
+      pCur->eOp = DELTAPARSE_OP_COPY;
+      pCur->iNext = (int)(&z[1] - pCur->aDelta);
+      break;
+    }
+    case ':': {
+      z++;
+      pCur->a2 = (unsigned int)(z - pCur->aDelta);
+      pCur->eOp = DELTAPARSE_OP_INSERT;
+      pCur->iNext = (int)(&z[pCur->a1] - pCur->aDelta);
+      break;
+    }
+    case ';': {
+      pCur->eOp = DELTAPARSE_OP_CHECKSUM;
+      pCur->iNext = pCur->nDelta;
+      break;
+    }
+    default: {
+      if( pCur->iNext==pCur->nDelta ){
+        pCur->eOp = DELTAPARSE_OP_EOF;
+      }else{
+        pCur->eOp = DELTAPARSE_OP_ERROR;
+        pCur->iNext = pCur->nDelta;
+      }
+      break;
+    }
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Return values of columns for the row at which the deltaparsevtab_cursor
+** is currently pointing.
+*/
+static int deltaparsevtabColumn(
+  sqlite3_vtab_cursor *cur,   /* The cursor */
+  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
+  int i                       /* Which column to return */
+){
+  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
+  switch( i ){
+    case DELTAPARSEVTAB_OP: {
+      sqlite3_result_text(ctx, azOp[pCur->eOp], -1, SQLITE_STATIC);
+      break;
+    }
+    case DELTAPARSEVTAB_A1: {
+      sqlite3_result_int(ctx, pCur->a1);
+      break;
+    }
+    case DELTAPARSEVTAB_A2: {
+      if( pCur->eOp==DELTAPARSE_OP_COPY ){
+        sqlite3_result_int(ctx, pCur->a2);
+      }else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
+        sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
+                            SQLITE_TRANSIENT);
+      }
+      break;
+    }
+    case DELTAPARSEVTAB_DELTA: {
+      sqlite3_result_blob(ctx, pCur->aDelta, pCur->nDelta, SQLITE_TRANSIENT);
+      break;
+    }
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Return the rowid for the current row.  In this implementation, the
+** rowid is the same as the output value.
+*/
+static int deltaparsevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
+  *pRowid = pCur->iCursor;
+  return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the cursor has been moved off of the last
+** row of output.
+*/
+static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){
+  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
+  return pCur->eOp==DELTAPARSE_OP_EOF;
+}
+
+/*
+** This method is called to "rewind" the deltaparsevtab_cursor object back
+** to the first row of output.  This method is always called at least
+** once prior to any call to deltaparsevtabColumn() or deltaparsevtabRowid() or 
+** deltaparsevtabEof().
+*/
+static int deltaparsevtabFilter(
+  sqlite3_vtab_cursor *pVtabCursor, 
+  int idxNum, const char *idxStr,
+  int argc, sqlite3_value **argv
+){
+  deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor *)pVtabCursor;
+  const char *a;
+  int i = 0;
+  pCur->eOp = DELTAPARSE_OP_ERROR;
+  if( idxNum!=1 ){
+    return SQLITE_OK;
+  }
+  pCur->nDelta = sqlite3_value_bytes(argv[0]);
+  a = (const char*)sqlite3_value_blob(argv[0]);
+  if( pCur->nDelta==0 || a==0 ){
+    return SQLITE_OK;
+  }
+  pCur->aDelta = sqlite3_malloc64( pCur->nDelta+1 );
+  if( pCur->aDelta==0 ){
+    pCur->nDelta = 0;
+    return SQLITE_NOMEM;
+  }
+  memcpy(pCur->aDelta, a, pCur->nDelta);
+  pCur->aDelta[pCur->nDelta] = 0;
+  a = pCur->aDelta;
+  pCur->eOp = DELTAPARSE_OP_SIZE;
+  pCur->a1 = deltaGetInt(&a, &i);
+  if( a[0]!='\n' ){
+    pCur->eOp = DELTAPARSE_OP_ERROR;
+    pCur->a1 = pCur->a2 = 0;
+    pCur->iNext = pCur->nDelta;
+    return SQLITE_OK;
+  }
+  a++;
+  pCur->iNext = (unsigned int)(a - pCur->aDelta);
+  return SQLITE_OK;
+}
+
+/*
+** SQLite will invoke this method one or more times while planning a query
+** that uses the virtual table.  This routine needs to create
+** a query plan for each invocation and compute an estimated cost for that
+** plan.
+*/
+static int deltaparsevtabBestIndex(
+  sqlite3_vtab *tab,
+  sqlite3_index_info *pIdxInfo
+){
+  int i;
+  for(i=0; i<pIdxInfo->nConstraint; i++){
+    if( pIdxInfo->aConstraint[i].iColumn != DELTAPARSEVTAB_DELTA ) continue;
+    if( pIdxInfo->aConstraint[i].usable==0 ) continue;
+    if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+    pIdxInfo->aConstraintUsage[i].argvIndex = 1;
+    pIdxInfo->aConstraintUsage[i].omit = 1;
+    pIdxInfo->estimatedCost = (double)1;
+    pIdxInfo->estimatedRows = 10;
+    pIdxInfo->idxNum = 1;
+    return SQLITE_OK;
+  }
+  pIdxInfo->idxNum = 0;
+  pIdxInfo->estimatedCost = (double)0x7fffffff;
+  pIdxInfo->estimatedRows = 0x7fffffff;
+  return SQLITE_OK;
+}
+
+/*
+** This following structure defines all the methods for the 
+** virtual table.
+*/
+static sqlite3_module deltaparsevtabModule = {
+  /* iVersion    */ 0,
+  /* xCreate     */ 0,
+  /* xConnect    */ deltaparsevtabConnect,
+  /* xBestIndex  */ deltaparsevtabBestIndex,
+  /* xDisconnect */ deltaparsevtabDisconnect,
+  /* xDestroy    */ 0,
+  /* xOpen       */ deltaparsevtabOpen,
+  /* xClose      */ deltaparsevtabClose,
+  /* xFilter     */ deltaparsevtabFilter,
+  /* xNext       */ deltaparsevtabNext,
+  /* xEof        */ deltaparsevtabEof,
+  /* xColumn     */ deltaparsevtabColumn,
+  /* xRowid      */ deltaparsevtabRowid,
+  /* xUpdate     */ 0,
+  /* xBegin      */ 0,
+  /* xSync       */ 0,
+  /* xCommit     */ 0,
+  /* xRollback   */ 0,
+  /* xFindMethod */ 0,
+  /* xRename     */ 0,
+  /* xSavepoint  */ 0,
+  /* xRelease    */ 0,
+  /* xRollbackTo */ 0,
+  /* xShadowName */ 0
+};
+
+
 
 #ifdef _WIN32
 __declspec(dllexport)
@@ -787,5 +1031,8 @@ int sqlite3_fossildelta_init(
     rc = sqlite3_create_function(db, "delta_output_size", 1, SQLITE_UTF8, 0,
                                  deltaOutputSizeFunc, 0, 0);
   }
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_create_module(db, "delta_parse", &deltaparsevtabModule, 0);
+  }
   return rc;
 }
index dc80c37b3849f634bf8daa84a30d9d80a3961a78..3affb0af3f178cf375cbefa2c5f1cd75a8bf6d34 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sthe\sfossildelta.c\sextension\sin\sext/misc\swith\simplementations\sof\sthe\sFossil\ndelta\sfunctions.
-D 2019-02-19T18:39:16.475
+C Add\sthe\sdelta_parse(DELTA)\stable-valued\sfunction\sto\sthe\sfossildelta\nextension.
+D 2019-02-19T20:19:51.952
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in 178d8eb6840771149cee40b322d1b3be30d330198c522c903c1b66fb5a1bfca4
@@ -286,7 +286,7 @@ F ext/misc/dbdump.c baf6e37447c9d6968417b1cd34cbedb0b0ab3f91b5329501d8a8d5be3287
 F ext/misc/eval.c 4b4757592d00fd32e44c7a067e6a0e4839c81a4d57abc4131ee7806d1be3104e
 F ext/misc/explain.c d5c12962d79913ef774b297006872af1fccda388f61a11d37758f9179a09551f
 F ext/misc/fileio.c 592d6531d8413d81b25f5a47a45d7e310e455d33e03a64c6ae85724c6524a5d5
-F ext/misc/fossildelta.c 64619ac4ff0d865e01f25436fd1b82c3dd7f6bc6184c9a06e002b16a121cd652
+F ext/misc/fossildelta.c 990fcc25a41be22c648d3e362f5d4616562869c613e75790bf71fa7c54d029fa
 F ext/misc/fuzzer.c 9e79c337faffdd4c5fe4485467537438359b43e0858a40038d4300b894ff553f
 F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
 F ext/misc/json1.c 8af4672f43634257dbcfdb4515b4070325463d67c6968b4be1bd414de28d4d58
@@ -1805,7 +1805,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 12517d1b15da46bc90bd95bb9c161d7f2ecdd7f28b1b3a5ed4397939ef986061
-R e38db5ccc4de4d9df319413343ad8fcf
+P b80cafa6f8a5c6ff1dc9efd2f670777ab131ace2df1eb431cedc8cfa901baf18
+R 28b47243faa86a15a5dab72f9f6d43db
 U drh
-Z 993efe1b585ff08956441be0e9d0910b
+Z 704f00932f29d8380bf4a3f8f9e7b2af
index 2ff6534024f2ee7892a5aeb12d5e34478e313aac..57af7c8c3067e2743e05e589d72fbdd48eae41fa 100644 (file)
@@ -1 +1 @@
-b80cafa6f8a5c6ff1dc9efd2f670777ab131ace2df1eb431cedc8cfa901baf18
\ No newline at end of file
+d91fcc267bf1be795dfdb1fbfb40c2aea79ddff247a51d26462136c325b7a6d3
\ No newline at end of file