1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Add new sessions API sqlite3changegroup_add_change().

FossilOrigin-Name: 73647db1ddfdaf40cbf18f1e47c10b4a906489f6d34d7667f0f2ff532f1eb37c
This commit is contained in:
dan
2024-05-04 21:10:24 +00:00
parent c52c00f670
commit 5b80dbe6b3
5 changed files with 253 additions and 106 deletions

View File

@ -3685,14 +3685,14 @@ static int sessionChangesetNextOne(
p->rc = sessionInputBuffer(&p->in, 2); p->rc = sessionInputBuffer(&p->in, 2);
if( p->rc!=SQLITE_OK ) return p->rc; if( p->rc!=SQLITE_OK ) return p->rc;
sessionDiscardData(&p->in);
p->in.iCurrent = p->in.iNext;
/* If the iterator is already at the end of the changeset, return DONE. */ /* If the iterator is already at the end of the changeset, return DONE. */
if( p->in.iNext>=p->in.nData ){ if( p->in.iNext>=p->in.nData ){
return SQLITE_DONE; return SQLITE_DONE;
} }
sessionDiscardData(&p->in);
p->in.iCurrent = p->in.iNext;
op = p->in.aData[p->in.iNext++]; op = p->in.aData[p->in.iNext++];
while( op=='T' || op=='P' ){ while( op=='T' || op=='P' ){
if( pbNew ) *pbNew = 1; if( pbNew ) *pbNew = 1;
@ -5427,6 +5427,7 @@ struct sqlite3_changegroup {
int rc; /* Error code */ int rc; /* Error code */
int bPatch; /* True to accumulate patchsets */ int bPatch; /* True to accumulate patchsets */
SessionTable *pList; /* List of tables in current patch */ SessionTable *pList; /* List of tables in current patch */
SessionBuffer rec;
sqlite3 *db; /* Configured by changegroup_schema() */ sqlite3 *db; /* Configured by changegroup_schema() */
char *zDb; /* Configured by changegroup_schema() */ char *zDb; /* Configured by changegroup_schema() */
@ -5725,108 +5726,128 @@ static int sessionChangesetExtendRecord(
} }
/* /*
** Add all changes in the changeset traversed by the iterator passed as ** Locate or create a SessionTable object that may be used to add the
** the first argument to the changegroup hash tables. ** change currently pointed to by iterator pIter to changegroup pGrp.
** If successful, set output variable (*ppTab) to point to the table
** object and return SQLITE_OK. Otherwise, if some error occurs, return
** an SQLite error code and leave (*ppTab) set to NULL.
*/ */
static int sessionChangesetToHash( static int sessionChangesetFindTable(
sqlite3_changeset_iter *pIter, /* Iterator to read from */ sqlite3_changegroup *pGrp,
sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ const char *zTab,
int bRebase /* True if hash table is for rebasing */ sqlite3_changeset_iter *pIter,
SessionTable **ppTab
){ ){
u8 *aRec;
int nRec;
int rc = SQLITE_OK; int rc = SQLITE_OK;
SessionTable *pTab = 0; SessionTable *pTab = 0;
SessionBuffer rec = {0, 0, 0}; int nTab = (int)strlen(zTab);
u8 *abPK = 0;
int nCol = 0;
while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ *ppTab = 0;
const char *zNew; sqlite3changeset_pk(pIter, &abPK, &nCol);
int nCol;
int op;
int iHash;
int bIndirect;
SessionChange *pChange;
SessionChange *pExist = 0;
SessionChange **pp;
/* Ensure that only changesets, or only patchsets, but not a mixture /* Search the list for an existing table */
** of both, are being combined. It is an error to try to combine a for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
** changeset and a patchset. */ if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break;
if( pGrp->pList==0 ){ }
pGrp->bPatch = pIter->bPatchset;
}else if( pIter->bPatchset!=pGrp->bPatch ){ /* If one was not found above, create a new table now */
rc = SQLITE_ERROR; if( !pTab ){
break; SessionTable **ppNew;
pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nTab+1);
if( !pTab ){
return SQLITE_NOMEM;
} }
memset(pTab, 0, sizeof(SessionTable));
pTab->nCol = nCol;
pTab->abPK = (u8*)&pTab[1];
memcpy(pTab->abPK, abPK, nCol);
pTab->zName = (char*)&pTab->abPK[nCol];
memcpy(pTab->zName, zTab, nTab+1);
sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect); if( pGrp->db ){
if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){ pTab->nCol = 0;
/* Search the list for a matching table */ rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
int nNew = (int)strlen(zNew); if( rc ){
u8 *abPK; assert( pTab->azCol==0 );
sqlite3_free(pTab);
sqlite3changeset_pk(pIter, &abPK, 0); return rc;
for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break;
}
if( !pTab ){
SessionTable **ppTab;
pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1);
if( !pTab ){
rc = SQLITE_NOMEM;
break;
}
memset(pTab, 0, sizeof(SessionTable));
pTab->nCol = nCol;
pTab->abPK = (u8*)&pTab[1];
memcpy(pTab->abPK, abPK, nCol);
pTab->zName = (char*)&pTab->abPK[nCol];
memcpy(pTab->zName, zNew, nNew+1);
if( pGrp->db ){
pTab->nCol = 0;
rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
if( rc ){
assert( pTab->azCol==0 );
sqlite3_free(pTab);
break;
}
}
/* The new object must be linked on to the end of the list, not
** simply added to the start of it. This is to ensure that the
** tables within the output of sqlite3changegroup_output() are in
** the right order. */
for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext);
*ppTab = pTab;
}
if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
rc = SQLITE_SCHEMA;
break;
} }
} }
if( nCol<pTab->nCol ){ /* The new object must be linked on to the end of the list, not
assert( pGrp->db ); ** simply added to the start of it. This is to ensure that the
rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, &rec); ** tables within the output of sqlite3changegroup_output() are in
if( rc ) break; ** the right order. */
aRec = rec.aBuf; for(ppNew=&pGrp->pList; *ppNew; ppNew=&(*ppNew)->pNext);
nRec = rec.nBuf; *ppNew = pTab;
} }
if( sessionGrowHash(0, pIter->bPatchset, pTab) ){ /* Check that the table is compatible. */
rc = SQLITE_NOMEM; if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
break; rc = SQLITE_SCHEMA;
} }
*ppTab = pTab;
return rc;
}
/*
** Add the change currently indicated by iterator pIter to the hash table
** belonging to changegroup pGrp.
*/
static int sessionOneChangeToHash(
sqlite3_changegroup *pGrp,
sqlite3_changeset_iter *pIter,
int bRebase
){
int rc = SQLITE_OK;
int nCol = 0;
int op = 0;
int iHash = 0;
int bIndirect = 0;
SessionChange *pChange = 0;
SessionChange *pExist = 0;
SessionChange **pp = 0;
SessionTable *pTab = 0;
u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
/* Ensure that only changesets, or only patchsets, but not a mixture
** of both, are being combined. It is an error to try to combine a
** changeset and a patchset. */
if( pGrp->pList==0 ){
pGrp->bPatch = pIter->bPatchset;
}else if( pIter->bPatchset!=pGrp->bPatch ){
rc = SQLITE_ERROR;
}
if( rc==SQLITE_OK ){
const char *zTab = 0;
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
}
if( rc==SQLITE_OK && nCol<pTab->nCol ){
SessionBuffer *pBuf = &pGrp->rec;
rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf);
aRec = pBuf->aBuf;
nRec = pBuf->nBuf;
assert( pGrp->db );
}
if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){
rc = SQLITE_NOMEM;
}
if( rc==SQLITE_OK ){
/* Search for existing entry. If found, remove it from the hash table.
** Code below may link it back in. */
iHash = sessionChangeHash( iHash = sessionChangeHash(
pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange pTab, (pIter->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){ for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
int bPkOnly1 = 0; int bPkOnly1 = 0;
int bPkOnly2 = 0; int bPkOnly2 = 0;
@ -5841,19 +5862,41 @@ static int sessionChangesetToHash(
break; break;
} }
} }
}
if( rc==SQLITE_OK ){
rc = sessionChangeMerge(pTab, bRebase, rc = sessionChangeMerge(pTab, bRebase,
pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
); );
if( rc ) break; }
if( pChange ){ if( rc==SQLITE_OK && pChange ){
pChange->pNext = pTab->apChange[iHash]; pChange->pNext = pTab->apChange[iHash];
pTab->apChange[iHash] = pChange; pTab->apChange[iHash] = pChange;
pTab->nEntry++; pTab->nEntry++;
} }
if( rc==SQLITE_OK ) rc = pIter->rc;
return rc;
}
/*
** Add all changes in the changeset traversed by the iterator passed as
** the first argument to the changegroup hash tables.
*/
static int sessionChangesetToHash(
sqlite3_changeset_iter *pIter, /* Iterator to read from */
sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */
int bRebase /* True if hash table is for rebasing */
){
u8 *aRec;
int nRec;
int rc = SQLITE_OK;
while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){
rc = sessionOneChangeToHash(pGrp, pIter, bRebase);
if( rc!=SQLITE_OK ) break;
} }
sqlite3_free(rec.aBuf);
if( rc==SQLITE_OK ) rc = pIter->rc; if( rc==SQLITE_OK ) rc = pIter->rc;
return rc; return rc;
} }
@ -5981,6 +6024,20 @@ int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void *pData){
return rc; return rc;
} }
/*
** Add a single change to a changeset-group.
*/
int sqlite3changegroup_add_change(
sqlite3_changegroup *pGrp,
sqlite3_changeset_iter *pIter
){
if( pIter->in.iCurrent==pIter->in.iNext || pIter->rc!=SQLITE_OK ){
/* Iterator does not point to any valid entry. */
return SQLITE_ERROR;
}
return sessionChangesetToHash(pIter, pGrp, 0);
}
/* /*
** Obtain a buffer containing a changeset representing the concatenation ** Obtain a buffer containing a changeset representing the concatenation
** of all changesets added to the group so far. ** of all changesets added to the group so far.
@ -6030,6 +6087,7 @@ void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
if( pGrp ){ if( pGrp ){
sqlite3_free(pGrp->zDb); sqlite3_free(pGrp->zDb);
sessionDeleteTable(0, pGrp->pList); sessionDeleteTable(0, pGrp->pList);
sqlite3_free(pGrp->rec.aBuf);
sqlite3_free(pGrp); sqlite3_free(pGrp);
} }
} }
@ -6431,6 +6489,7 @@ int sqlite3rebaser_rebase_strm(
void sqlite3rebaser_delete(sqlite3_rebaser *p){ void sqlite3rebaser_delete(sqlite3_rebaser *p){
if( p ){ if( p ){
sessionDeleteTable(0, p->grp.pList); sessionDeleteTable(0, p->grp.pList);
sqlite3_free(p->grp.rec.aBuf);
sqlite3_free(p); sqlite3_free(p);
} }
} }

View File

@ -1057,6 +1057,25 @@ int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);
*/ */
int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
/*
** CAPI3REF: Add A Single Change To A Changegroup
** METHOD: sqlite3_changegroup
**
** This function adds the single change currently indicated by the iterator
** passed as the second argument to the changegroup object. The rules for
** adding the change are just as described for [sqlite3_changegroup_add()].
**
** The iterator must point to a valid entry when this function is called.
** If it does not, SQLITE_ERROR is returned and no change is added to the
** changegroup.
*/
int sqlite3changegroup_add_change(
sqlite3_changegroup*,
sqlite3_changeset_iter*
);
/* /*
** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** CAPI3REF: Obtain A Composite Changeset From A Changegroup
** METHOD: sqlite3_changegroup ** METHOD: sqlite3_changegroup

View File

@ -1585,6 +1585,71 @@ static int SQLITE_TCLAPI test_sqlite3changegroup(
return TCL_OK; return TCL_OK;
} }
typedef struct TestChangeIter TestChangeIter;
struct TestChangeIter {
sqlite3_changeset_iter *pIter;
};
static int SQLITE_TCLAPI test_iter_cmd(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
return TCL_OK;
}
/*
** Tclcmd: sqlite3changeset_start ?-invert? CHANGESET
*/
static int SQLITE_TCLAPI test_sqlite3changeset_start(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int isInvert = 0;
void *pChangeset = 0; /* Buffer containing changeset */
int nChangeset = 0; /* Size of buffer aChangeset in bytes */
TestChangeIter *pNew = 0;
sqlite3_changeset_iter *pIter = 0;
int flags = 0;
int rc = SQLITE_OK;
static int iCmd = 1;
char zCmd[64];
if( objc==3 ){
int n = 0;
const char *z = Tcl_GetStringFromObj(objv[1], &n);
isInvert = (n>=2 && sqlite3_strnicmp(z, "-invert", n)==0);
}
if( objc!=2 && (objc!=3 || !isInvert) ){
Tcl_WrongNumArgs(interp, 1, objv, "?-invert? CHANGESET");
return TCL_ERROR;
}
flags = isInvert ? SQLITE_CHANGESETSTART_INVERT : 0;
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[objc-1], &nChangeset);
rc = sqlite3changeset_start_v2(&pIter, nChangeset, pChangeset, flags);
if( rc!=SQLITE_OK ){
char *zErr = sqlite3_mprintf(
"error in sqlite3changeset_start_v2() - %d", rc
);
Tcl_AppendResult(interp, zErr, (char*)0);
return TCL_ERROR;
}
pNew = (TestChangeIter*)ckalloc(sizeof(TestChangeIter));
pNew->pIter = pIter;
sprintf(zCmd, "csiter%d", iCmd++);
Tcl_CreateObjCommand(interp, zCmd, test_iter_cmd, (void*)pNew, 0);
Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1));
return TCL_OK;
}
int TestSession_Init(Tcl_Interp *interp){ int TestSession_Init(Tcl_Interp *interp){
struct Cmd { struct Cmd {
const char *zCmd; const char *zCmd;
@ -1592,6 +1657,7 @@ int TestSession_Init(Tcl_Interp *interp){
} aCmd[] = { } aCmd[] = {
{ "sqlite3session", test_sqlite3session }, { "sqlite3session", test_sqlite3session },
{ "sqlite3changegroup", test_sqlite3changegroup }, { "sqlite3changegroup", test_sqlite3changegroup },
{ "sqlite3changeset_start", test_sqlite3changeset_start },
{ "sqlite3session_foreach", test_sqlite3session_foreach }, { "sqlite3session_foreach", test_sqlite3session_foreach },
{ "sqlite3changeset_invert", test_sqlite3changeset_invert }, { "sqlite3changeset_invert", test_sqlite3changeset_invert },
{ "sqlite3changeset_concat", test_sqlite3changeset_concat }, { "sqlite3changeset_concat", test_sqlite3changeset_concat },

View File

@ -1,5 +1,5 @@
C Add\stest\scases\sto\stest/in7.test.\sNo\scode\schanges. C Add\snew\ssessions\sAPI\ssqlite3changegroup_add_change().
D 2024-05-04T16:50:47.770 D 2024-05-04T21:10:24.120
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -582,9 +582,9 @@ F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a
F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795 F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
F ext/session/sqlite3session.c 829d468f0f3d2710aace56b0116a7ca3f414683ce78e3125ae5e21547a895078 F ext/session/sqlite3session.c 0cdf052460fe11fd00a5c3f83b4a7152ed6c72ff7dacd4664c30ce16579748d7
F ext/session/sqlite3session.h 4cf19a51975746d7cff2fdd74db8b769c570958e1c3639ac150d824ac1553b3e F ext/session/sqlite3session.h e36392d1087e42f4e691caff23cb99f4e9fc3b8b88ac7504dac3ea30883dc08b
F ext/session/test_session.c 7b94ad945cd4afe6c73ee935aeb3d44b4446186e1729362af616c7695a5283d9 F ext/session/test_session.c ba40d22840f3ed667448e34ab4eaa38e69f62f23af530c0f8f70a64ed92b039b
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
F ext/userauth/user-auth.txt ca7e9ee82ca4e1c1744295f8184dd70edfae1992865d26c64303f539eb6c084c F ext/userauth/user-auth.txt ca7e9ee82ca4e1c1744295f8184dd70edfae1992865d26c64303f539eb6c084c
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
@ -2188,8 +2188,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P b36d499e4cdb41a5d7e44a1c4347a059d7654f85ade9c5c04d18ac95ddc09fde P 8c3086f9fe502dfc4a1fa610a23794fb037721df29dc5d2126cfb749a9d44a50
R 960c98e5734bcdd93c02bad4a02cdc67 R 92b682ff0cdf49829f0c1fddda225a0c
T *branch * changegroup-add-change
T *sym-changegroup-add-change *
T -sym-trunk *
U dan U dan
Z a883d658d4cc670b0c73f4c6321f0eb9 Z d18fc76401ae213ec8fe7cdec8b2f0ce
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
8c3086f9fe502dfc4a1fa610a23794fb037721df29dc5d2126cfb749a9d44a50 73647db1ddfdaf40cbf18f1e47c10b4a906489f6d34d7667f0f2ff532f1eb37c