]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add largely untested APIs for rebasing changesets.
authordan <dan@noemail.net>
Wed, 14 Mar 2018 21:06:58 +0000 (21:06 +0000)
committerdan <dan@noemail.net>
Wed, 14 Mar 2018 21:06:58 +0000 (21:06 +0000)
FossilOrigin-Name: 39915b683b3f8d3bf872af1dede96bf2818b488a8638a1d248395023fc4bd0ef

ext/session/sqlite3session.c
ext/session/sqlite3session.h
ext/session/test_session.c
manifest
manifest.uuid

index c8d66bb5be3739db99ebf2a7dde3adc1741c64e3..ad898e8844cf0a812ba78c2437e4d3d0a7cd4f89 100644 (file)
@@ -2918,7 +2918,8 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
 static int sessionChangesetNext(
   sqlite3_changeset_iter *p,      /* Changeset iterator */
   u8 **paRec,                     /* If non-NULL, store record pointer here */
-  int *pnRec                      /* If non-NULL, store size of record here */
+  int *pnRec,                     /* If non-NULL, store size of record here */
+  int *pbNew                      /* If non-NULL, true if new table */
 ){
   int i;
   u8 op;
@@ -2953,6 +2954,7 @@ static int sessionChangesetNext(
 
   op = p->in.aData[p->in.iNext++];
   while( op=='T' || op=='P' ){
+    if( pbNew ) *pbNew = 1;
     p->bPatchset = (op=='P');
     if( sessionChangesetReadTblhdr(p) ) return p->rc;
     if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc;
@@ -3031,7 +3033,7 @@ static int sessionChangesetNext(
 ** callback by changeset_apply().
 */
 int sqlite3changeset_next(sqlite3_changeset_iter *p){
-  return sessionChangesetNext(p, 0, 0);
+  return sessionChangesetNext(p, 0, 0, 0);
 }
 
 /*
@@ -4522,6 +4524,7 @@ struct sqlite3_changegroup {
 */
 static int sessionChangeMerge(
   SessionTable *pTab,             /* Table structure */
+  int bRebase,                    /* True for a rebase hash-table */
   int bPatchset,                  /* True for patchsets */
   SessionChange *pExist,          /* Existing change */
   int op2,                        /* Second change operation */
@@ -4543,6 +4546,8 @@ static int sessionChangeMerge(
     pNew->nRecord = nRec;
     pNew->aRecord = (u8*)&pNew[1];
     memcpy(pNew->aRecord, aRec, nRec);
+  }else if( bRebase){
+    assert( 0 );
   }else{
     int op1 = pExist->op;
 
@@ -4645,7 +4650,8 @@ static int sessionChangeMerge(
 */
 static int sessionChangesetToHash(
   sqlite3_changeset_iter *pIter,   /* Iterator to read from */
-  sqlite3_changegroup *pGrp        /* Changegroup object to add changeset to */
+  sqlite3_changegroup *pGrp,       /* Changegroup object to add changeset to */
+  int bRebase                      /* True if hash table is for rebasing */
 ){
   u8 *aRec;
   int nRec;
@@ -4653,7 +4659,7 @@ static int sessionChangesetToHash(
   SessionTable *pTab = 0;
 
 
-  while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec) ){
+  while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){
     const char *zNew;
     int nCol;
     int op;
@@ -4733,7 +4739,7 @@ static int sessionChangesetToHash(
       }
     }
 
-    rc = sessionChangeMerge(pTab, 
+    rc = sessionChangeMerge(pTab, bRebase, 
         pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
     );
     if( rc ) break;
@@ -4841,7 +4847,7 @@ int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void *pData){
 
   rc = sqlite3changeset_start(&pIter, nData, pData);
   if( rc==SQLITE_OK ){
-    rc = sessionChangesetToHash(pIter, pGrp);
+    rc = sessionChangesetToHash(pIter, pGrp, 0);
   }
   sqlite3changeset_finalize(pIter);
   return rc;
@@ -4872,7 +4878,7 @@ int sqlite3changegroup_add_strm(
 
   rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
   if( rc==SQLITE_OK ){
-    rc = sessionChangesetToHash(pIter, pGrp);
+    rc = sessionChangesetToHash(pIter, pGrp, 0);
   }
   sqlite3changeset_finalize(pIter);
   return rc;
@@ -4957,4 +4963,256 @@ int sqlite3changeset_concat_strm(
   return rc;
 }
 
+struct sqlite3_rebaser {
+  sqlite3_changegroup grp;        /* Hash table */
+};
+
+/*
+** Buffers a1 and a2 must both contain a sessions module record nCol
+** fields in size. This function appends an nCol sessions module 
+** record to buffer pBuf that is a copy of a1, except that:
+**
+**   + If bUndefined is 0, for each field that is not "undefined" in either
+**     a1[] or a2[], swap in the field from a2[].
+**
+**   + If bUndefined is 1, for each field that is "undefined" in a1[]
+**     swap in the field from a2[].
+*/
+static void sessionAppendRecordMerge(
+  SessionBuffer *pBuf,
+  int nCol,
+  int bUndefined,
+  u8 *a1, int n1,
+  u8 *a2, int n2,
+  int *pRc
+){
+  sessionBufferGrow(pBuf, n1+n2, pRc);
+  if( *pRc==SQLITE_OK ){
+    int i;
+    u8 *pOut = &pBuf->aBuf[pBuf->nBuf];
+    for(i=0; i<nCol; i++){
+      int nn1 = sessionSerialLen(a1);
+      int nn2 = sessionSerialLen(a2);
+      if( bUndefined==0 ){
+        if( *a1 && *a2 ){
+          memcpy(pOut, a2, nn2);
+          pOut += nn2;
+        }else{
+          memcpy(pOut, a1, nn1);
+          pOut += nn1;
+        }
+      }else{
+        if( *a1==0 ){
+          memcpy(pOut, a2, nn2);
+          pOut += nn2;
+        }else{
+          memcpy(pOut, a1, nn1);
+          pOut += nn1;
+        }
+      }
+      a1 += n1;
+      a2 += n2;
+    }
+  }
+}
+
+static int sessionRebase(
+  sqlite3_rebaser *p,             /* Rebaser hash table */
+  sqlite3_changeset_iter *pIter,  /* Input data */
+  int (*xOutput)(void *pOut, const void *pData, int nData),
+  void *pOut,                     /* Context for xOutput callback */
+  int *pnOut,                     /* OUT: Number of bytes in output changeset */
+  void **ppOut                    /* OUT: Inverse of pChangeset */
+){
+  int rc = SQLITE_OK;
+  u8 *aRec = 0;
+  int nRec = 0;
+  int bNew = 0;
+  SessionTable *pTab = 0;
+  SessionBuffer sOut = {0,0,0};
+
+  while( SQLITE_OK==sessionChangesetNext(pIter, &aRec, &nRec, &bNew) ){
+    SessionChange *pChange = 0;
+
+    if( bNew ){
+      const char *zTab = pIter->zTab;
+      for(pTab=p->grp.pList; pTab; pTab=pTab->pNext){
+        if( 0==sqlite3_stricmp(pTab->zName, zTab) ) break;
+      }
+      bNew = 0;
+
+      /* Append a table header to the output for this new table */
+      sessionAppendByte(&sOut, pIter->bPatchset ? 'P' : 'T', &rc);
+      sessionAppendVarint(&sOut, pIter->nCol, &rc);
+      sessionAppendBlob(&sOut, pIter->abPK, pIter->nCol, &rc);
+      sessionAppendBlob(&sOut, (u8*)pIter->zTab, strlen(pIter->zTab)+1, &rc);
+    }
+
+    if( pTab ){
+      int bPkOnly = (pIter->bPatchset && pIter->op==SQLITE_DELETE);
+      int iHash = sessionChangeHash(pTab, bPkOnly, aRec, pTab->nChange);
+
+      for(pChange=pTab->apChange[iHash]; pChange; pChange=pChange->pNext){
+        if( sessionChangeEqual(pTab, bPkOnly, aRec, 0, pChange->aRecord) ){
+          break;
+        }
+      }
+    }
+
+    if( pChange ){
+      assert( pChange->op==SQLITE_DELETE || pChange->op==SQLITE_INSERT );
+      /* If pChange is an INSERT, then rebase the change. If it is a
+      ** DELETE, omit the change from the output altogether.  */
+      if( pChange->op==SQLITE_INSERT ){
+        if( pChange->bIndirect ){
+          /* The change being rebased against was a DELETE. So, if the
+          ** input is a:
+          **
+          **   DELETE - omit the change altogether.
+          **   UPDATE - change to an INSERT,
+          **   INSERT - no change (output the record as is).
+          */
+          if( pIter->op!=SQLITE_DELETE ){
+            sessionAppendByte(&sOut, SQLITE_INSERT, &rc);
+            sessionAppendByte(&sOut, pIter->bIndirect, &rc);
+            if( pIter->op==SQLITE_INSERT ){
+              sessionAppendBlob(&sOut, aRec, nRec, &rc);
+            }else{
+              sessionAppendRecordMerge(&sOut, pIter->nCol, 1,
+                  aRec, nRec, pChange->aRecord, pChange->nRecord, &rc
+              );
+            }
+          }
+        }else{
+          sessionAppendByte(&sOut, pIter->op, &rc);
+          sessionAppendByte(&sOut, pIter->bIndirect, &rc);
+          if( pIter->op==SQLITE_INSERT ){
+            sessionAppendBlob(&sOut, aRec, nRec, &rc);
+          }else{
+            u8 *pCsr = aRec;
+            sessionAppendRecordMerge(&sOut, pIter->nCol, 0,
+                aRec, nRec, pChange->aRecord, pChange->nRecord, &rc
+            );
+            if( pIter->op==SQLITE_UPDATE ){
+              sessionSkipRecord(&pCsr, pIter->nCol);
+              sessionAppendBlob(&sOut, pCsr, nRec - (pCsr-aRec), &rc);
+            }
+          }
+        }
+      }
+    }else{
+      sessionAppendByte(&sOut, pIter->op, &rc);
+      sessionAppendByte(&sOut, pIter->bIndirect, &rc);
+      sessionAppendBlob(&sOut, aRec, nRec, &rc);
+    }
+
+    if( rc==SQLITE_OK && xOutput && sOut.nBuf>SESSIONS_STRM_CHUNK_SIZE ){
+      rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
+      sOut.nBuf = 0;
+    }
+    if( rc ) break;
+  }
+
+  if( rc!=SQLITE_OK ){
+    sqlite3_free(sOut.aBuf);
+    memset(&sOut, 0, sizeof(sOut));
+  }
+
+  if( rc==SQLITE_OK ){
+    if( xOutput ){
+      if( sOut.nBuf>0 ){
+        rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
+      }
+    }else{
+      *ppOut = (void*)sOut.aBuf;
+      *pnOut = sOut.nBuf;
+      sOut.aBuf = 0;
+    }
+  }
+  sqlite3_free(sOut.aBuf);
+  return rc;
+}
+
+/* 
+** Create a new rebaser object.
+*/
+int sqlite3rebaser_create(sqlite3_rebaser **ppNew){
+  int rc = SQLITE_OK;
+  sqlite3_rebaser *pNew;
+
+  pNew = sqlite3_malloc(sizeof(sqlite3_rebaser));
+  if( pNew==0 ){
+    rc = SQLITE_NOMEM;
+  }
+  *ppNew = pNew;
+  return rc;
+}
+
+/* 
+** Call this one or more times to configure a rebaser.
+*/
+int sqlite3rebaser_configure(
+  sqlite3_rebaser *p, 
+  int nRebase, const void *pRebase
+){
+  sqlite3_changeset_iter *pIter = 0;   /* Iterator opened on pData/nData */
+  int rc;                              /* Return code */
+  rc = sqlite3changeset_start(&pIter, nRebase, (void*)pRebase);
+  if( rc==SQLITE_OK ){
+    rc = sessionChangesetToHash(pIter, &p->grp, 1);
+  }
+  sqlite3changeset_finalize(pIter);
+  return rc;
+}
+
+/* 
+** Rebase a changeset according to current rebaser configuration 
+*/
+int sqlite3rebaser_rebase(
+  sqlite3_rebaser *p,
+  int nIn, const void *pIn, 
+  int *pnOut, void **ppOut 
+){
+  sqlite3_changeset_iter *pIter = 0;   /* Iterator to skip through input */  
+  int rc = sqlite3changeset_start(&pIter, nIn, (void*)pIn);
+
+  if( rc==SQLITE_OK ){
+    rc = sessionRebase(p, pIter, 0, 0, pnOut, ppOut);
+    sqlite3changeset_finalize(pIter);
+  }
+
+  return rc;
+}
+
+/* 
+** Rebase a changeset according to current rebaser configuration 
+*/
+int sqlite3rebaser_rebase_strm(
+  sqlite3_rebaser *p,
+  int (*xInput)(void *pIn, void *pData, int *pnData),
+  void *pIn,
+  int (*xOutput)(void *pOut, const void *pData, int nData),
+  void *pOut
+){
+  sqlite3_changeset_iter *pIter = 0;   /* Iterator to skip through input */  
+  int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
+
+  if( rc==SQLITE_OK ){
+    rc = sessionRebase(p, pIter, xOutput, pOut, 0, 0);
+    sqlite3changeset_finalize(pIter);
+  }
+
+  return rc;
+}
+
+/* 
+** Destroy a rebaser object 
+*/
+void sqlite3rebaser_destroy(sqlite3_rebaser *p){
+  if( p ){
+    sessionDeleteTable(p->grp.pList);
+    sqlite3_free(p);
+  }
+}
+
 #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
index c6a2332b8d5722171b29bc886fae285e793bf421..a7281bd4e7b11d25c121605cc57b9e23f3198e14 100644 (file)
@@ -1216,6 +1216,27 @@ int sqlite3changeset_apply_v2(
 #define SQLITE_CHANGESET_REPLACE    1
 #define SQLITE_CHANGESET_ABORT      2
 
+typedef struct sqlite3_rebaser sqlite3_rebaser;
+
+/* Create a new rebaser object */
+int sqlite3rebaser_create(sqlite3_rebaser **ppNew);
+
+/* Call this one or more times to configure a rebaser */
+int sqlite3rebaser_configure(
+  sqlite3_rebaser*, 
+  int nRebase, const void *pRebase
+); 
+
+/* Rebase a changeset according to current rebaser configuration */
+int sqlite3rebaser_rebase(
+  sqlite3_rebaser*,
+  int nIn, const void *pIn, 
+  int *pnOut, void **ppOut 
+);
+
+/* Destroy a rebaser object */
+void sqlite3rebaser_destroy(sqlite3_rebaser *p); 
+
 /*
 ** CAPI3REF: Streaming Versions of API functions.
 **
@@ -1373,6 +1394,13 @@ int sqlite3changegroup_output_strm(sqlite3_changegroup*,
     int (*xOutput)(void *pOut, const void *pData, int nData), 
     void *pOut
 );
+int sqlite3rebaser_rebase_strm(
+  sqlite3_rebaser *pRebaser,
+  int (*xInput)(void *pIn, void *pData, int *pnData),
+  void *pIn,
+  int (*xOutput)(void *pOut, const void *pData, int nData),
+  void *pOut
+);
 
 
 /*
index dd2d9977aa8276563babe6168afa6670e24a3f15..7b08267b537610e8059215b8a2eefe587338a4d4 100644 (file)
@@ -761,9 +761,18 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
   }else{
     sStr.aData = (unsigned char*)pChangeset;
     sStr.nData = nChangeset;
-    rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
-        (objc==5) ? test_filter_handler : 0, test_conflict_handler, (void *)&ctx
-    );
+    if( bV2==0 ){
+      rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
+          (objc==5) ? test_filter_handler : 0, 
+          test_conflict_handler, (void *)&ctx
+      );
+    }else{
+      rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr,
+          (objc==5) ? test_filter_handler : 0, 
+          test_conflict_handler, (void *)&ctx,
+          &pRebase, &nRebase
+      );
+    }
   }
 
   if( rc!=SQLITE_OK ){
index 5e08ac632cc5bc4da31d76df809577a4d3f6382a..b887be3c0afdbcdd17da026b73cb7f6d68266fc8 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\ssqlite3_changeset_apply_v2()\sand\sapply_v2_strm()\sto\sthe\ssessions\smodule.
-D 2018-03-13T20:31:23.940
+C Add\slargely\suntested\sAPIs\sfor\srebasing\schangesets.
+D 2018-03-14T21:06:58.004
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3
@@ -403,9 +403,9 @@ F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0
 F ext/session/sessionrebase.test b4ac7545e3c69deaeab061c2bf36ad9e99aa6c38db94c340d7e48a230a9d4be8
 F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e
 F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc
-F ext/session/sqlite3session.c 564e609f3086510d319e8abc0899fa79131d071b7dc01138e1b75bbf4a43cb03
-F ext/session/sqlite3session.h 8fe499b736633021094b814cf128691587f19c1943e372a23db37175bdeb8c67
-F ext/session/test_session.c 6c45bee58063f2c3d1edc213833d72f0a3ec651cbe27393b9e4a054e711b7c44
+F ext/session/sqlite3session.c 16561ad7eb8270bd3d2ee42c434043e68831bbd452fbea82d83922a1066a7cc8
+F ext/session/sqlite3session.h 74ba48151f3593a66a975ac095d7b53efa6c1e12fe83a903e10bf8d85a1429dd
+F ext/session/test_session.c 8c04dc8cada82bd4e12f18ada3e35b56a8fd4d8dee7caac324ae28091c2b492f
 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
 F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
@@ -1713,10 +1713,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 0171d4a71ca7911a9fd409a42eeed0eda4521b6e48df5cd058364c0a736313b7
-R 1a6994480e1e2c2b73e68977f31547a3
-T *branch * sessions-rebase
-T *sym-sessions-rebase *
-T -sym-trunk *
+P 445bfe977d9f3a891e08ef33237862ed047fe83e134ef3ed8b47ee0f5abd8cd6
+R 485a23056d6edbdb25721aa9069ac291
 U dan
-Z d8bbc56681a1d6ff1f4fb97dc44d1b66
+Z e2563dee69381fb6fb02a6ddaa823fec
index 1e1f7573de0446eb03cb3ffe73b53ad863e2a87a..7b1553cdcdf3b7277b70bf9bf84ad3aa1f04bd4c 100644 (file)
@@ -1 +1 @@
-445bfe977d9f3a891e08ef33237862ed047fe83e134ef3ed8b47ee0f5abd8cd6
\ No newline at end of file
+39915b683b3f8d3bf872af1dede96bf2818b488a8638a1d248395023fc4bd0ef
\ No newline at end of file