]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fixes for the sqlite3changeset_concat() API regarding patchsets.
authordan <dan@noemail.net>
Sat, 16 Aug 2014 16:47:55 +0000 (16:47 +0000)
committerdan <dan@noemail.net>
Sat, 16 Aug 2014 16:47:55 +0000 (16:47 +0000)
FossilOrigin-Name: dccb34859508eac8146ae5b19c447673d04be3b0

ext/session/sessionB.test
ext/session/sqlite3session.c
manifest
manifest.uuid

index 76213d8ab2c44b03aea553adf212c37c0f627fa1..8791272f8e31a0a6a6c626434edc43bd697de5c2 100644 (file)
@@ -178,17 +178,24 @@ do_test 3.2 {
 reset_db
 
 proc do_patchconcat_test {tn args} {
+  set bRevert 0
+  if {[lindex $args 0] == "-revert"} {
+    set bRevert 1
+    set args [lrange $args 1 end]
+  }
   set nSql [expr [llength $args]-1]
   set res [lindex $args $nSql]
   set patchlist [list]
 
   execsql BEGIN
+  if {$bRevert} { execsql { SAVEPOINT x } }
   foreach sql [lrange $args 0 end-1] {
     sqlite3session S db main
     S attach *
     execsql $sql
     lappend patchlist [S patchset]
     S delete
+    if {$bRevert} { execsql { ROLLBACK TO x } }
   }
   execsql ROLLBACK
 
@@ -215,17 +222,109 @@ do_patchconcat_test 4.1.2 {
   {INSERT t1 0 X.. {} {i 4 i 5 i 6}}
 }
 
-if 0 {
 do_execsql_test 4.2.1 {
   INSERT INTO t1 VALUES(1, 2, 3);
   INSERT INTO t1 VALUES(4, 5, 6);
 }
+
 do_patchconcat_test 4.2.2 {
   UPDATE t1 SET z = 'abc' WHERE x=1
 } {
   UPDATE t1 SET z = 'def' WHERE x=4
 } {
+  {UPDATE t1 0 X.. {i 1 {} {} {} {}} {{} {} {} {} t abc}} 
+  {UPDATE t1 0 X.. {i 4 {} {} {} {}} {{} {} {} {} t def}}
+}
+
+do_patchconcat_test 4.2.3 {
+  DELETE FROM t1 WHERE x=1;
+} {
+  DELETE FROM t1 WHERE x=4;
+} {
+  {DELETE t1 0 X.. {i 1 {} {} {} {}} {}} 
+  {DELETE t1 0 X.. {i 4 {} {} {} {}} {}}
+}
+
+
+do_execsql_test 4.3.1 {
+  CREATE TABLE t2(a, b, c, d, PRIMARY KEY(c, b));
+  INSERT INTO t2 VALUES('.', 1, 1, '.');
+  INSERT INTO t2 VALUES('.', 1, 2, '.');
+  INSERT INTO t2 VALUES('.', 2, 1, '.');
+  INSERT INTO t2 VALUES('.', 2, 2, '.');
+}
+
+# INSERT + INSERT 
+do_patchconcat_test 4.3.2 -revert {
+  INSERT INTO t2 VALUES('a', 'a', 'a', 'a');
+} {
+  INSERT INTO t2 VALUES('b', 'a', 'a', 'b');
+} {
+  {INSERT t2 0 .XX. {} {t a t a t a t a}}
+}
+
+# INSERT + DELETE 
+do_patchconcat_test 4.3.3 {
+  INSERT INTO t2 VALUES('a', 'a', 'a', 'a');
+} {
+  DELETE FROM t2 WHERE c = 'a';
+} {
+}
+
+# INSERT + UPDATE
+do_patchconcat_test 4.3.4 {
+  INSERT INTO t2 VALUES('a', 'a', 'a', 'a');
+} {
+  UPDATE t2 SET d = 'b' WHERE c='a';
+} {
+  {INSERT t2 0 .XX. {} {t a t a t a t b}}
+}
+
+# UPDATE + UPDATE
+do_patchconcat_test 4.3.5 {
+  UPDATE t2 SET a = 'a' WHERE c=1 AND b=2;
+} {
+  UPDATE t2 SET d = 'd' WHERE c=1 AND b=2;
+} {
+  {UPDATE t2 0 .XX. {{} {} i 2 i 1 {} {}} {t a {} {} {} {} t d}}
+}
+
+# UPDATE + DELETE
+do_patchconcat_test 4.3.6 {
+  UPDATE t2 SET a = 'a' WHERE c=1 AND b=2;
+} {
+  DELETE FROM t2 WHERE c=1 AND b=2;
+} {
+  {DELETE t2 0 .XX. {{} {} i 2 i 1 {} {}} {}}
+}
+
+# DELETE + INSERT
+do_patchconcat_test 4.3.7 {
+  DELETE FROM t2 WHERE b=1;
+} {
+  INSERT INTO t2 VALUES('x', 1, 2, '.');
+} {
+  {DELETE t2 0 .XX. {{} {} i 1 i 1 {} {}} {}} 
+  {UPDATE t2 0 .XX. {{} {} i 1 i 2 {} {}} {t x {} {} {} {} t .}}
+}
+
+# DELETE + UPDATE
+do_patchconcat_test 4.3.8 -revert {
+  DELETE FROM t2 WHERE b=1 AND c=2;
+} {
+  UPDATE t2 SET a=5 WHERE b=1 AND c=2;
+} {
+  {DELETE t2 0 .XX. {{} {} i 1 i 2 {} {}} {}} 
 }
+
+# DELETE + UPDATE
+do_patchconcat_test 4.3.9 -revert {
+  DELETE FROM t2 WHERE b=1 AND c=2;
+} {
+  DELETE FROM t2 WHERE b=1;
+} {
+  {DELETE t2 0 .XX. {{} {} i 1 i 1 {} {}} {}} 
+  {DELETE t2 0 .XX. {{} {} i 1 i 2 {} {}} {}} 
 }
 
 finish_test
index 62adb8de2536e41c1997846be6c267d3e73349e6..b86c635ed17b7f9e81dd749c712f4b7cc3d1d82b 100644 (file)
@@ -443,9 +443,13 @@ static int sessionSerialLen(u8 *a){
 ** hash key. Assume the has table has nBucket buckets. The hash keys
 ** calculated by this function are compatible with those calculated by
 ** sessionPreupdateHash().
+**
+** The bPkOnly argument is non-zero if the record at aRecord[] is from
+** a patchset DELETE. In this case the non-PK fields are omitted entirely.
 */
 static unsigned int sessionChangeHash(
   SessionTable *pTab,             /* Table handle */
+  int bPkOnly,                    /* Record consists of PK fields only */
   u8 *aRecord,                    /* Change record */
   int nBucket                     /* Assume this many buckets in hash table */
 ){
@@ -456,6 +460,7 @@ static unsigned int sessionChangeHash(
   for(i=0; i<pTab->nCol; i++){
     int eType = *a;
     int isPK = pTab->abPK[i];
+    if( bPkOnly && isPK==0 ) continue;
 
     /* It is not possible for eType to be SQLITE_NULL here. The session 
     ** module does not record changes for rows with NULL values stored in
@@ -493,7 +498,9 @@ static unsigned int sessionChangeHash(
 */
 static int sessionChangeEqual(
   SessionTable *pTab,             /* Table used for PK definition */
+  int bLeftPkOnly,
   u8 *aLeft,                      /* Change record */
+  int bRightPkOnly,
   u8 *aRight                      /* Change record */
 ){
   u8 *a1 = aLeft;                 /* Cursor to iterate through aLeft */
@@ -507,8 +514,8 @@ static int sessionChangeEqual(
     if( pTab->abPK[iCol] && (n1!=n2 || memcmp(a1, a2, n1)) ){
       return 0;
     }
-    a1 += n1;
-    a2 += n2;
+    if( pTab->abPK[iCol] || bLeftPkOnly==0 ) a1 += n1;
+    if( pTab->abPK[iCol] || bRightPkOnly==0 ) a2 += n2;
   }
 
   return 1;
@@ -606,6 +613,7 @@ static u8 *sessionMergeValue(
 static int sessionMergeUpdate(
   u8 **paOut,                     /* IN/OUT: Pointer to output buffer */
   SessionTable *pTab,             /* Table change pertains to */
+  int bPatchset,
   u8 *aOldRecord1,                /* old.* record for first change */
   u8 *aOldRecord2,                /* old.* record for second change */
   u8 *aNewRecord1,                /* new.* record for first change */
@@ -618,29 +626,32 @@ static int sessionMergeUpdate(
 
   u8 *aOut = *paOut;
   int i;
-  int bRequired = 0;
 
-  assert( aOldRecord1 && aNewRecord1 );
-
-  /* Write the old.* vector first. */
-  for(i=0; i<pTab->nCol; i++){
-    int nOld;
-    u8 *aOld;
-    int nNew;
-    u8 *aNew;
-
-    aOld = sessionMergeValue(&aOld1, &aOld2, &nOld);
-    aNew = sessionMergeValue(&aNew1, &aNew2, &nNew);
-    if( pTab->abPK[i] || nOld!=nNew || memcmp(aOld, aNew, nNew) ){
-      if( pTab->abPK[i]==0 ) bRequired = 1;
-      memcpy(aOut, aOld, nOld);
-      aOut += nOld;
-    }else{
-      *(aOut++) = '\0';
+  if( bPatchset==0 ){
+    int bRequired = 0;
+
+    assert( aOldRecord1 && aNewRecord1 );
+
+    /* Write the old.* vector first. */
+    for(i=0; i<pTab->nCol; i++){
+      int nOld;
+      u8 *aOld;
+      int nNew;
+      u8 *aNew;
+
+      aOld = sessionMergeValue(&aOld1, &aOld2, &nOld);
+      aNew = sessionMergeValue(&aNew1, &aNew2, &nNew);
+      if( pTab->abPK[i] || nOld!=nNew || memcmp(aOld, aNew, nNew) ){
+        if( pTab->abPK[i]==0 ) bRequired = 1;
+        memcpy(aOut, aOld, nOld);
+        aOut += nOld;
+      }else{
+        *(aOut++) = '\0';
+      }
     }
-  }
 
-  if( !bRequired ) return 0;
+    if( !bRequired ) return 0;
+  }
 
   /* Write the new.* vector */
   aOld1 = aOldRecord1;
@@ -655,7 +666,9 @@ static int sessionMergeUpdate(
 
     aOld = sessionMergeValue(&aOld1, &aOld2, &nOld);
     aNew = sessionMergeValue(&aNew1, &aNew2, &nNew);
-    if( pTab->abPK[i] || (nOld==nNew && 0==memcmp(aOld, aNew, nNew)) ){
+    if( bPatchset==0 
+     && (pTab->abPK[i] || (nOld==nNew && 0==memcmp(aOld, aNew, nNew))) 
+    ){
       *(aOut++) = '\0';
     }else{
       memcpy(aOut, aNew, nNew);
@@ -754,7 +767,7 @@ static int sessionPreupdateEqual(
 ** Growing the hash table in this case is a performance optimization only,
 ** it is not required for correct operation.
 */
-static int sessionGrowHash(SessionTable *pTab){
+static int sessionGrowHash(int bPatchset, SessionTable *pTab){
   if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
     int i;
     SessionChange **apNew;
@@ -773,7 +786,8 @@ static int sessionGrowHash(SessionTable *pTab){
       SessionChange *p;
       SessionChange *pNext;
       for(p=pTab->apChange[i]; p; p=pNext){
-        int iHash = sessionChangeHash(pTab, p->aRecord, nNew);
+        int bPkOnly = (p->op==SQLITE_DELETE && bPatchset);
+        int iHash = sessionChangeHash(pTab, bPkOnly, p->aRecord, nNew);
         pNext = p->pNext;
         p->pNext = apNew[iHash];
         apNew[iHash] = p;
@@ -960,7 +974,7 @@ static void sessionPreupdateOneChange(
   if( sessionInitTable(pSession, pTab) ) return;
 
   /* Grow the hash table if required */
-  if( sessionGrowHash(pTab) ){
+  if( sessionGrowHash(0, pTab) ){
     pSession->rc = SQLITE_NOMEM;
     return;
   }
@@ -3174,6 +3188,7 @@ int sqlite3changeset_apply(
 */
 static int sessionChangeMerge(
   SessionTable *pTab,             /* Table structure */
+  int bPatchset,                  /* True for patchsets */
   SessionChange *pExist,          /* Existing change */
   int op2,                        /* Second change operation */
   int bIndirect,                  /* True if second change is indirect */
@@ -3219,9 +3234,13 @@ static int sessionChangeMerge(
       sqlite3_free(pExist);
       assert( pNew==0 );
     }else{
+      u8 *aExist = pExist->aRecord;
       int nByte;
       u8 *aCsr;
 
+      /* Allocate a new SessionChange object. Ensure that the aRecord[]
+      ** buffer of the new object is large enough to hold any record that
+      ** may be generated by combining the input records.  */
       nByte = sizeof(SessionChange) + pExist->nRecord + nRec;
       pNew = (SessionChange *)sqlite3_malloc(nByte);
       if( !pNew ){
@@ -3236,30 +3255,37 @@ static int sessionChangeMerge(
         u8 *a1 = aRec;
         assert( op2==SQLITE_UPDATE );
         pNew->op = SQLITE_INSERT;
-        sessionReadRecord(&a1, pTab->nCol, 0, 0);
-        sessionMergeRecord(&aCsr, pTab->nCol, pExist->aRecord, a1);
+        if( bPatchset==0 ) sessionReadRecord(&a1, pTab->nCol, 0, 0);
+        sessionMergeRecord(&aCsr, pTab->nCol, aExist, a1);
       }else if( op1==SQLITE_DELETE ){       /* DELETE + INSERT */
         assert( op2==SQLITE_INSERT );
         pNew->op = SQLITE_UPDATE;
-        if( 0==sessionMergeUpdate(&aCsr, pTab, pExist->aRecord, 0, aRec, 0) ){
+        if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aExist, 0, aRec, 0) ){
           sqlite3_free(pNew);
           pNew = 0;
         }
       }else if( op2==SQLITE_UPDATE ){       /* UPDATE + UPDATE */
-        u8 *a1 = pExist->aRecord;
+        u8 *a1 = aExist;
         u8 *a2 = aRec;
         assert( op1==SQLITE_UPDATE );
-        sessionReadRecord(&a1, pTab->nCol, 0, 0);
-        sessionReadRecord(&a2, pTab->nCol, 0, 0);
+        if( bPatchset==0 ){
+          sessionReadRecord(&a1, pTab->nCol, 0, 0);
+          sessionReadRecord(&a2, pTab->nCol, 0, 0);
+        }
         pNew->op = SQLITE_UPDATE;
-        if( 0==sessionMergeUpdate(&aCsr, pTab, aRec, pExist->aRecord, a1, a2) ){
+        if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aRec, aExist,a1,a2) ){
           sqlite3_free(pNew);
           pNew = 0;
         }
       }else{                                /* UPDATE + DELETE */
         assert( op1==SQLITE_UPDATE && op2==SQLITE_DELETE );
         pNew->op = SQLITE_DELETE;
-        sessionMergeRecord(&aCsr, pTab->nCol, aRec, pExist->aRecord);
+        if( bPatchset ){
+          memcpy(aCsr, aRec, nRec);
+          aCsr += nRec;
+        }else{
+          sessionMergeRecord(&aCsr, pTab->nCol, aRec, aExist);
+        }
       }
 
       if( pNew ){
@@ -3278,6 +3304,7 @@ static int sessionChangeMerge(
 ** hash tables.
 */
 static int sessionConcatChangeset(
+  int bPatchset,                  /* True to expect patchsets */
   int nChangeset,                 /* Number of bytes in pChangeset */
   void *pChangeset,               /* Changeset buffer */
   SessionTable **ppTabList        /* IN/OUT: List of table objects */
@@ -3301,6 +3328,13 @@ static int sessionConcatChangeset(
     SessionChange *pExist = 0;
     SessionChange **pp;
 
+    assert( bPatchset==0 || bPatchset==1 );
+    assert( pIter->bPatchset==0 || pIter->bPatchset==1 );
+    if( pIter->bPatchset!=bPatchset ){
+      rc = SQLITE_ERROR;
+      break;
+    }
+
     assert( pIter->apValue==0 );
     sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect);
 
@@ -3335,17 +3369,25 @@ static int sessionConcatChangeset(
       pTab->zName = (char *)zNew;
     }
 
-    if( sessionGrowHash(pTab) ){
+    if( sessionGrowHash(bPatchset, pTab) ){
       rc = SQLITE_NOMEM;
       break;
     }
-    iHash = sessionChangeHash(pTab, aRec, pTab->nChange);
+    iHash = sessionChangeHash(
+        pTab, (bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
+    );
 
     /* Search for existing entry. If found, remove it from the hash table. 
     ** Code below may link it back in.
     */
     for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
-      if( sessionChangeEqual(pTab, (*pp)->aRecord, aRec) ){
+      int bPkOnly1 = 0;
+      int bPkOnly2 = 0;
+      if( bPatchset ){
+        bPkOnly1 = (*pp)->op==SQLITE_DELETE;
+        bPkOnly2 = op==SQLITE_DELETE;
+      }
+      if( sessionChangeEqual(pTab, bPkOnly1, (*pp)->aRecord, bPkOnly2, aRec) ){
         pExist = *pp;
         *pp = (*pp)->pNext;
         pTab->nEntry--;
@@ -3353,7 +3395,9 @@ static int sessionConcatChangeset(
       }
     }
 
-    rc = sessionChangeMerge(pTab, pExist, op, bIndirect, aRec, nRec, &pChange);
+    rc = sessionChangeMerge(pTab, 
+        bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
+    );
     if( rc ) break;
     if( pChange ){
       pChange->pNext = pTab->apChange[iHash];
@@ -3392,13 +3436,15 @@ int sqlite3changeset_concat(
 ){
   SessionTable *pList = 0;        /* List of SessionTable objects */
   int rc;                         /* Return code */
+  int bPatch;                     /* True for a patchset */
 
   *pnOut = 0;
   *ppOut = 0;
+  bPatch = (nLeft>0 && *(char*)pLeft=='P') || (nRight>0 && *(char*)pRight=='P');
 
-  rc = sessionConcatChangeset(nLeft, pLeft, &pList);
+  rc = sessionConcatChangeset(bPatch, nLeft, pLeft, &pList);
   if( rc==SQLITE_OK ){
-    rc = sessionConcatChangeset(nRight, pRight, &pList);
+    rc = sessionConcatChangeset(bPatch, nRight, pRight, &pList);
   }
 
   /* Create the serialized output changeset based on the contents of the
@@ -3411,7 +3457,7 @@ int sqlite3changeset_concat(
       int i;
       if( pTab->nEntry==0 ) continue;
 
-      sessionAppendTableHdr(&buf, 0, pTab, &rc);
+      sessionAppendTableHdr(&buf, bPatch, pTab, &rc);
       for(i=0; i<pTab->nChange; i++){
         SessionChange *p;
         for(p=pTab->apChange[i]; p; p=p->pNext){
index 833a47196f1f70ce8497428f2daae5a8f3b14ef2..2e5dc07be7c1ebbb481d406ba30fa4d8bda605dd 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Begin\sadding\sthe\ssqlite3session_patchset()\sAPI\sto\sthe\ssessions\sextension.\sThis\sis\san\sinterim\scommit.
-D 2014-08-15T20:15:49.367
+C Fixes\sfor\sthe\ssqlite3changeset_concat()\sAPI\sregarding\spatchsets.
+D 2014-08-16T16:47:55.133
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 639859a6f81bd15921ccd56ddbd6dfd335278377
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -152,10 +152,10 @@ F ext/session/session6.test 443789bc2fca12e4f7075cf692c60b8a2bea1a26
 F ext/session/session8.test 7d35947ad329b8966f095d34f9617a9eff52dc65
 F ext/session/session9.test 776e46785c29c11cda01f5205d0f1e8f8f9a46bf
 F ext/session/sessionA.test eb05c13e4ef1ca8046a3a6dbf2d5f6f5b04a11d4
-F ext/session/sessionB.test dbabf40e7580f2dc245ea6b37b94fd1ec0b25fc3
+F ext/session/sessionB.test 51c70394bc04ac72d72f54bd0879614b9c34240c
 F ext/session/session_common.tcl 1539d8973b2aea0025c133eb0cc4c89fcef541a5
 F ext/session/sessionfault.test 496291b287ba3c0b14ca2e074425e29cc92a64a6
-F ext/session/sqlite3session.c e0d8101afc5df85495c401eadf2cb288c3d53adc
+F ext/session/sqlite3session.c eed0f0510859e564f9f8c886d22a299579b5c6c1
 F ext/session/sqlite3session.h c99445ea9918343d3e62acafdf82bc5502cfba29
 F ext/session/test_session.c 920ccb6d6e1df263cd9099563328094c230b2925
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
@@ -1202,7 +1202,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 2acbeac1fd9b9feb26e1c24d4ae50ce79f17a3f8
-R 3c0b82c93c7620c8b999c9243f48a749
+P 60a4565a8c44762a002cd02979317df5ca47e899
+R 1bd074b437e8898f047cce1541823103
 U dan
-Z a8da13936a56069d46e167f9c70b2c2a
+Z 66bd65019d7460afe376babf330097ba
index ccaf220a3e85ae632a51e043ceaa211d30aefdca..9786ee73ae2b0ba573582739d38b4a12338d2ff7 100644 (file)
@@ -1 +1 @@
-60a4565a8c44762a002cd02979317df5ca47e899
\ No newline at end of file
+dccb34859508eac8146ae5b19c447673d04be3b0
\ No newline at end of file