grp delete
}
-
do_test 3.2.1 {
sqlite3changegroup grp
grp schema db main
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
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.
*/
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,
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;
}
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;
}
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++){
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",
}
}
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"
);
}
}
+ 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;
pGrp->cd.record.aBuf[pGrp->cd.record.nBuf++] = eUndef;
}
}
+ }
+ if( rc==SQLITE_OK ){
rc = sessionOneChangeToHash(
pGrp,
pGrp->cd.pTab,
}
}
+ /* Reset all aBuf[] entries to "undefined". */
{
int nZero = pGrp->cd.pTab->nCol;
if( pGrp->cd.eOp==SQLITE_UPDATE ) nZero += nZero;
*/
#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
**
** 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.
** 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
{ "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;
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]));
-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
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
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
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.
-27150a8c22dc5331efa9e97637eae3741682bc14ae993f0b7672ccc63c37f1f9
+8699fe5b24c8aeb5e552aa7f4995f2b50dd33d1febd23ec1c07c8fc72497da72