]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Allow the APIs on this branch to be used to create patchsets as well as changesets.
authordan <Dan Kennedy>
Tue, 13 Jan 2026 19:59:47 +0000 (19:59 +0000)
committerdan <Dan Kennedy>
Tue, 13 Jan 2026 19:59:47 +0000 (19:59 +0000)
FossilOrigin-Name: 8699fe5b24c8aeb5e552aa7f4995f2b50dd33d1febd23ec1c07c8fc72497da72

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

index 4a211d8c9378402b3c832ef632aa821bd7c2f725..6a508ef221132c6d5a7103de38f8b05d01ecfb08 100644 (file)
@@ -186,7 +186,6 @@ foreach {tn script error} {
   grp delete
 }
 
-
 do_test 3.2.1 {
   sqlite3changegroup grp
   grp schema db main
@@ -201,5 +200,133 @@ do_test 3.2.2 {
 
 grp delete
 
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 3.0 {
+  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+  CREATE TABLE t2(a, b);
+}
+
+proc do_changegroup_script_test {tn script changeset patchset} {
+
+breakpoint
+  sqlite3changegroup grp
+  grp schema db main
+  eval $script
+  do_test $tn.1 {
+    changeset_to_list [grp output]
+  } [list {*}$changeset]
+  grp delete
+
+  sqlite3changegroup grp
+  grp config patchset 1
+  grp schema db main
+  eval $script
+  do_test $tn.2 {
+    changeset_to_list [grp output]
+  } [list {*}$patchset]
+  grp delete
+
+}
+
+proc do_changegroup_patchset_test {tn script patchset} {
+
+  sqlite3changegroup grp
+  grp config patchset 1
+  grp schema db main
+  eval $script
+  do_test $tn {
+    changeset_to_list [grp output]
+  } [list {*}$patchset]
+  grp delete
+
+}
+
+do_changegroup_script_test 3.1 {
+  grp change_begin UPDATE t1 0
+  grp change_int64 old 0 123
+  grp change_int64 old 1 456
+  grp change_int64 new 1 789
+  grp change_finish false
+
+  grp change_begin INSERT t1 0
+  grp change_int64 new 0 124
+  grp change_text  new 1 hello_world
+  grp change_finish false
+
+  grp change_begin  DELETE t1 0
+  grp change_int64  old 0 125
+  grp change_double old 1 45.67
+  grp change_finish false
+} {
+  {DELETE t1 0 X. {i 125 f 45.67} {}}
+  {INSERT t1 0 X. {} {i 124 t hello_world}}
+  {UPDATE t1 0 X. {i 123 i 456} {{} {} i 789}}
+} {
+  {DELETE t1 0 X. {i 125 {} {}} {}}
+  {INSERT t1 0 X. {} {i 124 t hello_world}}
+  {UPDATE t1 0 X. {i 123 {} {}} {{} {} i 789}}
+}
+
+do_changegroup_script_test 3.1.2 {
+  grp change_begin INSERT t1 0
+  grp change_int64 new 0 124
+  grp change_text  new 1 hello_world
+  grp change_finish false
+} {
+  {INSERT t1 0 X. {} {i 124 t hello_world}}
+} {
+  {INSERT t1 0 X. {} {i 124 t hello_world}}
+}
+
+do_changegroup_patchset_test 3.2 {
+  grp change_begin UPDATE t1 0
+  grp change_int64 old 0 123
+  grp change_int64 new 1 789
+  grp change_finish false
+
+  grp change_begin DELETE t1 1
+  grp change_int64 old 0 122
+  grp change_finish false
+} {
+  {DELETE t1 1 X. {i 122 {} {}} {}}
+  {UPDATE t1 0 X. {i 123 {} {}} {{} {} i 789}}
+}
+
+do_changegroup_patchset_test 3.3 {
+  grp change_begin DELETE t1 0
+  grp change_int64 old 0 123
+  grp change_finish false
+} {
+  {DELETE t1 0 X. {i 123 {} {}} {}}
+}
+
+do_changegroup_script_test 3.4 {
+  grp change_begin INSERT t1 0
+  grp change_int64 new 0 124
+  grp change_text  new 1 hello_world
+  grp change_finish false
+
+  grp change_begin DELETE t1 0
+  grp change_int64 old 0 124
+  grp change_text  old 1 hello_world
+  grp change_finish false
+
+  grp change_begin INSERT t1 0
+  grp change_int64 new 0 1000
+  grp change_blob  new 1 abcd
+  grp change_finish false
+
+  grp change_begin UPDATE t1 0
+  grp change_int64 old 0 1000
+  grp change_blob  old 1 abcd
+  grp change_blob  new 1 bcda
+  grp change_finish false
+} {
+  {INSERT t1 0 X. {} {i 1000 b bcda}}
+} {
+  {INSERT t1 0 X. {} {i 1000 b bcda}}
+}
+
 finish_test
 
index 278810e12cdd9dff78981a205589fb316017641f..feabdf5c12856f92f23991f5edc37f191d24a589 100644 (file)
@@ -6271,6 +6271,33 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp){
   return rc;
 }
 
+/*
+** Configure a changegroup object.
+*/
+int sqlite3changegroup_config(
+  sqlite3_changegroup *pGrp, 
+  int op, 
+  void *pArg
+){
+  int rc = SQLITE_OK;
+
+  switch( op ){
+    case SQLITE_CHANGEGROUP_CONFIG_PATCHSET: {
+      int arg = *(int*)pArg;
+      if( pGrp->pList==0  && arg>=0 ){
+        pGrp->bPatch = (arg>0);
+      }
+      *(int*)pArg = pGrp->bPatch;
+      break;
+    }
+    default:
+      rc = SQLITE_MISUSE;
+      break;
+  }
+
+  return rc;
+}
+
 /*
 ** Provide a database schema to the changegroup object.
 */
@@ -6879,6 +6906,10 @@ int sqlite3changegroup_change_begin(
   return rc;
 }
 
+/*
+** This function does processing common to the _change_int64(), _change_text()
+** and other similar APIs.
+*/
 static int checkChangeParams(
   sqlite3_changegroup *pGrp, 
   int bNew,
@@ -6992,6 +7023,7 @@ int sqlite3changegroup_change_text(
   pBuf->aBuf[0] = SQLITE_TEXT;
   pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nText));
   memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nText);
+  pBuf->nBuf += nText;
 
   return SQLITE_OK;
 }
@@ -7017,6 +7049,7 @@ int sqlite3changegroup_change_blob(
   pBuf->aBuf[0] = SQLITE_BLOB;
   pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nVal));
   memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nVal);
+  pBuf->nBuf += nVal;
 
   return SQLITE_OK;
 }
@@ -7033,7 +7066,6 @@ int sqlite3changegroup_change_finish(
 
     if( bDiscard==0 ){
       int nBuf = pGrp->cd.pTab->nCol;
-      int nRec = 0;
       u8 eUndef = SQLITE_NULL;
       if( pGrp->cd.eOp==SQLITE_UPDATE ){
         for(ii=0; ii<nBuf; ii++){
@@ -7052,7 +7084,8 @@ int sqlite3changegroup_change_finish(
               rc = SQLITE_ERROR;
               break;
             }
-          }else if( (aBuf[ii].nBuf>0)!=(aBuf[ii+nBuf].nBuf>0) ){
+          }else 
+          if( pGrp->bPatch==0 && (aBuf[ii].nBuf>0)!=(aBuf[ii+nBuf].nBuf>0) ){
             *pzErr = sqlite3_mprintf(
                 "invalid change: column %d "
                 "- old.* value is %sdefined but new.* is %sdefined",
@@ -7063,17 +7096,20 @@ int sqlite3changegroup_change_finish(
           }
         }
         eUndef = 0x00;
-        nBuf = nBuf * 2;
+        if( pGrp->bPatch==0 ) nBuf = nBuf * 2;
       }else{
         for(ii=0; ii<nBuf; ii++){
-          if( aBuf[ii].nBuf==0 ){
+          int isPK = pGrp->cd.pTab->abPK[ii];
+          if( (pGrp->cd.eOp==SQLITE_INSERT || pGrp->bPatch==0 || isPK)
+           && aBuf[ii].nBuf==0
+          ){
             *pzErr = sqlite3_mprintf(
                 "invalid change: column %d is undefined", ii
             );
             rc = SQLITE_ERROR;
             break;
           }
-          if( aBuf[ii].nBuf==1 && pGrp->cd.pTab->abPK[ii] ){
+          if( aBuf[ii].nBuf==1 && isPK ){
             *pzErr = sqlite3_mprintf(
                 "invalid change: null value in PK"
             );
@@ -7083,13 +7119,19 @@ int sqlite3changegroup_change_finish(
         }
       }
 
+      pGrp->cd.record.nBuf = 0;
       for(ii=0; ii<nBuf; ii++){
-        SessionBuffer *pBuf = &pGrp->cd.aBuf[ii];
-        nRec += (pBuf->nBuf ? pBuf->nBuf : 1);
-      }
-      if( 0==sessionBufferGrow(&pGrp->cd.record, nRec, &rc) ){
-        for(ii=0; ii<nBuf; ii++){
-          SessionBuffer *p = &pGrp->cd.aBuf[ii];
+        SessionBuffer *p = &pGrp->cd.aBuf[ii];
+        if( pGrp->bPatch ){
+          if( pGrp->cd.pTab->abPK[ii]==0 ){
+            if( pGrp->cd.eOp==SQLITE_UPDATE ){
+              p += pGrp->cd.pTab->nCol;
+            }else if( pGrp->cd.eOp==SQLITE_DELETE ){
+              continue;
+            }
+          }
+        }
+        if( 0==sessionBufferGrow(&pGrp->cd.record, p->nBuf?p->nBuf:1, &rc) ){
           if( p->nBuf ){
             memcpy(&pGrp->cd.record.aBuf[pGrp->cd.record.nBuf],p->aBuf,p->nBuf);
             pGrp->cd.record.nBuf += p->nBuf;
@@ -7097,6 +7139,8 @@ int sqlite3changegroup_change_finish(
             pGrp->cd.record.aBuf[pGrp->cd.record.nBuf++] = eUndef;
           }
         }
+      }
+      if( rc==SQLITE_OK ){
         rc = sessionOneChangeToHash(
             pGrp,
             pGrp->cd.pTab,
@@ -7110,6 +7154,7 @@ int sqlite3changegroup_change_finish(
       }
     }
 
+    /* Reset all aBuf[] entries to "undefined". */
     {
       int nZero = pGrp->cd.pTab->nCol;
       if( pGrp->cd.eOp==SQLITE_UPDATE ) nZero += nZero;
index 1c3af0e0b7c843d8cec86d2978860e8cc95a7ec7..36dfc504e9bbd6ee3005b5814048006a0f3db441 100644 (file)
@@ -1853,6 +1853,43 @@ int sqlite3session_config(int op, void *pArg);
 */
 #define SQLITE_SESSION_CONFIG_STRMSIZE 1
 
+/*
+** CAPI3REF: Configure a changegroup object
+**
+** Configure the changegroup object passes as the first argument. 
+** At present the only valid value for the second parameter is
+** [SQLITE_CHANGEGROUP_CONFIG_PATCHSET].
+*/
+int sqlite3changegroup_config(sqlite3_changegroup*, int, void *pArg);
+
+/*
+** CAPI3REF: Options for sqlite3changegroup_config().
+**
+** The following values may passed as the the 2nd parameter to
+** sqlite3session_object_config().
+**
+** <dt>SQLITE_CHANGEGROUP_CONFIG_PATCHSET <dd>
+**   A changegroup object generates either a changeset or patchset. Usually,
+**   which is determined by whether the first call to sqlite3changegroup_add()
+**   is passed a changeset or a patchset. Or, if the first changes are added
+**   to the changegroup object using the sqlite3changegroup_change_xxx()
+**   APIs, then this option may be used to configure whether the changegroup
+**   object generates a changeset or patchset. 
+**
+**   When this option is invoked, parameter pArg must point to a value of
+**   type int. If the changegroup currently contains zero changes, and the
+**   value of the int variable is zero or greater than zero, then the
+**   changegroup is configured to generate a changeset or patchset,
+**   respectively. It is not an error if the changegroup is not configured
+**   because it has already started accumuting changes, just a no-op.
+**
+**   Before returning, the int variable is set to 0 if the changegroup is
+**   configured to generate a changeset, or 1 if it is configured to generate
+**   a patchset.
+*/
+#define SQLITE_CHANGEGROUP_CONFIG_PATCHSET 1
+
+
 /*
 ** CAPI3REF: Begin adding a change to a changegroup
 **
@@ -1982,7 +2019,8 @@ int sqlite3changegroup_change_blob(
 ** returned.
 **
 ** If paramter bDiscard is zero, then an attempt is made to add the current
-** change to the changegroup. This requires that:
+** change to the changegroup. Assuming the changegroup is configured to
+** produce a changeset (not a patchset), this requires that:
 **
 **   *  If the change is an INSERT or DELETE, then a value must be specified
 **      for all columns of the new.* or old.* record, respectively.
@@ -2005,6 +2043,12 @@ int sqlite3changegroup_change_blob(
 ** current change is combined with the existing change as for 
 ** sqlite3changegroup_add().
 **
+** For a patchset, all of the above rules apply except that it doesn't matter
+** whether or not values are provided for the non-PK old.* record columns
+** for an UPDATE or DELETE change. This means that code used to produce
+** a changeset using the sqlite3changegroup_change_xxx() APIs may also
+** be used to produce patchsets.
+**
 ** If the call is successful, SQLITE_OK is returned. Otherwise, if an error
 ** occurs, an SQLite error code is returned. If an error is returned and
 ** parameter pzErr is not NULL, then (*pzErr) may be set to point to a buffer
index d868db375807b899859ef0d3e361187a48928e57..03929284db6d4c2ccc0d5f9c3415131c37afbd12 100644 (file)
@@ -1570,6 +1570,8 @@ static int SQLITE_TCLAPI test_changegroup_cmd(
     { "change_text",   3, "[new|old] ICOL VALUE" },    /* 9 */
     { "change_blob",   3, "[new|old] ICOL VALUE" },    /* 10 */
     { "change_finish", 1, "BDISCARD"             },    /* 11 */
+
+    { "config",        2, "OPTION INTVAL"        },    /* 12 */
     { 0, 0, 0 }
   };
   int rc = TCL_OK;
@@ -1778,6 +1780,35 @@ static int SQLITE_TCLAPI test_changegroup_cmd(
       break;
     }
 
+    case 12: {      /* config */
+      struct OptionName {
+        const char *zOpt;
+        int op;
+      } aOp[] = {
+        { "patchset", SQLITE_CHANGEGROUP_CONFIG_PATCHSET },
+        { 0, 0 }
+      };
+      int iIdx = 0;
+      int iArg = 0;
+      rc = Tcl_GetIndexFromObjStruct(
+          interp, objv[2], aOp, sizeof(aOp[0]), "option", 0, &iIdx
+      );
+      if( rc==TCL_OK 
+       && (rc = Tcl_GetIntFromObj(interp, objv[3], &iArg))==TCL_OK 
+      ){
+        int op = aOp[iIdx].op;
+        void *pArg = (void*)&iArg;
+
+        rc = sqlite3changegroup_config(p->pGrp, op, pArg);
+        if( rc!=SQLITE_OK ){
+          rc = test_session_error(interp, rc, 0);
+        }else{
+          Tcl_SetObjResult(interp, Tcl_NewIntObj(iArg));
+        }
+      }
+      break;
+    }
+
     default: {     /* delete */
       assert( iSub==3 );
       Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
index e8378621b923f7d8eb866c25e15ef983cf1196c5..c4852e1c4a34352811f3cc859434c85687e7ad4a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\san\sAPI\sto\sthe\ssessions\smodule\sto\sadd\schanges\sone\sat\sa\stime\sto\san\ssqlite3_changegroup\sobject.
-D 2026-01-12T16:39:27.740
+C Allow\sthe\sAPIs\son\sthis\sbranch\sto\sbe\sused\sto\screate\spatchsets\sas\swell\sas\schangesets.
+D 2026-01-13T19:59:47.818
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -562,7 +562,7 @@ F ext/session/sessionat.test 00c8badb35e43a2f12a716d2734a44d614ff62361979b6b8541
 F ext/session/sessionbig.test 47c381e7acfabeef17d98519a3080d69151723354d220afa2053852182ca7adf
 F ext/session/sessionblob.test 87faf667870b72f08e91969abd9f52a383ab7b514506ee194d64a39d8faff00a
 F ext/session/sessionchange.test 6618cb1c1338a4b6df173b6ac42d09623fb71269962abf23ebb7617fe9f45a50
-F ext/session/sessionchange2.test 9f8eef9a673e9cdc0c0d8eee0fe4f27295ae6e491a449542a44ba23e763c4258
+F ext/session/sessionchange2.test 601d8151b7faeef300355b9eaea65c26d823746e83ccaa8d674198a12d6641dd
 F ext/session/sessionconflict.test 19e4a53795c4c930bfec49e809311e09b2a9e202d9446e56d7a8b139046a0c07 x
 F ext/session/sessiondiff.test e89f7aedcdd89e5ebac3a455224eb553a171e9586fc3e1e6a7b3388d2648ba8d
 F ext/session/sessionfault.test c2b43d01213b389a3f518e90775fca2120812ba51e50444c4066962263e45c11
@@ -578,9 +578,9 @@ F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a
 F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
 F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec
 F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
-F ext/session/sqlite3session.c f13ff9db3d1a492820fe9845739561a9336f066a606adf14ed679a7a6450bfa8
-F ext/session/sqlite3session.h d45d8dcc32bd0454b65fbb08d2a86f6d68d918f3c9dbf01dbe8540ee81954003
-F ext/session/test_session.c 86f5dae26de28f7091fc4913b4b8880ce764f24fb8d76eafba8c0a33125b2462
+F ext/session/sqlite3session.c 16c36e63d41e2d0f6a8d4593618f221185064ab01d49b8514cb5e710642bdb67
+F ext/session/sqlite3session.h cb6d25b573751afd9668f9ae8f1c7819df30916f43cc4c06b57749a6c2ce3e10
+F ext/session/test_session.c c4164b818cbdfaa3ebbcde31a9a620a63478478154715276ce09bced8918429b
 F ext/wasm/GNUmakefile c3d007dd181527283d8674c812cc60518353f1f69c9a9d3008f10f53cea4a3c1
 F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
 F ext/wasm/README.md 2e87804e12c98f1d194b7a06162a88441d33bb443efcfe00dc6565a780d2f259
@@ -2192,11 +2192,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
 F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P b57a8215f4259a0aae188b7ee5060f8ff48919303179aae80b58b43ed3b991f5
-R 3bc6861aec8f49b745d80fe907b58d8c
-T *branch * changegroup-change-api
-T *sym-changegroup-change-api *
-T -sym-trunk *
+P 27150a8c22dc5331efa9e97637eae3741682bc14ae993f0b7672ccc63c37f1f9
+R f6f93392cfb2ac7fdf4dfeb41e3a044d
 U dan
-Z a079c2735ef0be18051333989922e875
+Z b3689361f6f700b24a921b70fab2026e
 # Remove this line to create a well-formed Fossil manifest.
index 4f43bd95513bce5f9437aea697018e89e7cc0ae4..4b3655ff91330c6fafcb366750a81a4be9cb3475 100644 (file)
@@ -1 +1 @@
-27150a8c22dc5331efa9e97637eae3741682bc14ae993f0b7672ccc63c37f1f9
+8699fe5b24c8aeb5e552aa7f4995f2b50dd33d1febd23ec1c07c8fc72497da72