return rc;
}
+/*
+** sqlite3_changegroup handle.
+*/
+struct sqlite3_changegroup {
+ int rc; /* Error code */
+ int bPatch; /* True to accumulate patchsets */
+ SessionTable *pList; /* List of tables in current patch */
+};
+
/*
** This function is called to merge two changes to the same row together as
** part of an sqlite3changeset_concat() operation. A new change object is
}
/*
-** Add all changes in the changeset passed via the first two arguments to
-** hash tables.
+** 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 */
- SessionTable **ppTabList /* IN/OUT: List of table objects */
+ sqlite3_changegroup *pGrp /* Changegroup object to add changeset to */
){
u8 *aRec;
int nRec;
int rc = SQLITE_OK;
SessionTable *pTab = 0;
+
while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec) ){
const char *zNew;
int nCol;
SessionChange *pExist = 0;
SessionChange **pp;
+ if( pGrp->pList==0 ){
+ pGrp->bPatch = pIter->bPatchset;
+ }else if( pIter->bPatchset!=pGrp->bPatch ){
+ rc = SQLITE_ERROR;
+ break;
+ }
+
sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect);
if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){
/* Search the list for a matching table */
u8 *abPK;
sqlite3changeset_pk(pIter, &abPK, 0);
- for(pTab = *ppTabList; pTab; pTab=pTab->pNext){
+ for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break;
}
if( !pTab ){
break;
}
memset(pTab, 0, sizeof(SessionTable));
- pTab->pNext = *ppTabList;
+ pTab->pNext = pGrp->pList;
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);
- *ppTabList = pTab;
+ pGrp->pList = pTab;
}else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){
rc = SQLITE_SCHEMA;
break;
if( rc==SQLITE_OK ) rc = pIter->rc;
return rc;
}
-
-/*
-** 1. Iterate through the left-hand changeset. Add an entry to a table
-** specific hash table for each change in the changeset. The hash table
-** key is the PK of the row affected by the change.
+
+/*
+** Serialize a changeset (or patchset) based on all changesets (or patchsets)
+** added to the changegroup object passed as the first argument.
**
-** 2. Then interate through the right-hand changeset. Attempt to add an
-** entry to a hash table for each component change. If a change already
-** exists with the same PK values, combine the two into a single change.
+** If xOutput is not NULL, then the changeset/patchset is returned to the
+** user via one or more calls to xOutput, as with the other streaming
+** interfaces.
**
-** 3. Write an output changeset based on the contents of the hash table.
+** Or, if xOutput is NULL, then (*ppOut) is populated with a pointer to a
+** buffer containing the output changeset before this function returns. In
+** this case (*pnOut) is set to the size of the output buffer in bytes. It
+** is the responsibility of the caller to free the output buffer using
+** sqlite3_free() when it is no longer required.
+**
+** If successful, SQLITE_OK is returned. Or, if an error occurs, an SQLite
+** error code. If an error occurs and xOutput is NULL, (*ppOut) and (*pnOut)
+** are both set to 0 before returning.
*/
-static int sessionChangesetConcat(
- sqlite3_changeset_iter *pLeft,
- sqlite3_changeset_iter *pRight,
+static int sessionChangegroupOutput(
+ sqlite3_changegroup *pGrp,
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut,
int *pnOut,
void **ppOut
){
- SessionTable *pList = 0; /* List of SessionTable objects */
- int rc; /* Return code */
- int bPatch; /* True for a patchset */
- SessionTable *pTab;
+ int rc = SQLITE_OK;
SessionBuffer buf = {0, 0, 0};
-
+ SessionTable *pTab;
assert( xOutput==0 || (ppOut==0 && pnOut==0) );
- assert( pLeft->zTab==0 && pRight->zTab==0 );
- rc = sessionChangesetToHash(pLeft, &pList);
- assert( pLeft->zTab || pList==0 );
- if( rc==SQLITE_OK ){
- rc = sessionChangesetToHash(pRight, &pList);
- }
- bPatch = pLeft->bPatchset || pRight->bPatchset;
-
- if( pLeft->zTab && pRight->zTab && pLeft->bPatchset!=pRight->bPatchset ){
- rc = SQLITE_ERROR;
- }
-
/* Create the serialized output changeset based on the contents of the
- ** hash tables attached to the SessionTable objects in list pList.
+ ** hash tables attached to the SessionTable objects in list p->pList.
*/
- for(pTab=pList; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
+ for(pTab=pGrp->pList; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
int i;
if( pTab->nEntry==0 ) continue;
- sessionAppendTableHdr(&buf, bPatch, pTab, &rc);
+ sessionAppendTableHdr(&buf, pGrp->bPatch, pTab, &rc);
for(i=0; i<pTab->nChange; i++){
SessionChange *p;
for(p=pTab->apChange[i]; p; p=p->pNext){
}
sqlite3_free(buf.aBuf);
- sessionDeleteTable(pList);
return rc;
}
+/*
+** Allocate a new, empty, sqlite3_changegroup.
+*/
+int sqlite3changegroup_new(sqlite3_changegroup **pp){
+ int rc = SQLITE_OK; /* Return code */
+ sqlite3_changegroup *p; /* New object */
+ p = (sqlite3_changegroup*)sqlite3_malloc(sizeof(sqlite3_changegroup));
+ if( p==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(p, 0, sizeof(sqlite3_changegroup));
+ }
+ *pp = p;
+ return rc;
+}
+
+/*
+** Add the changeset currently stored in buffer pData, size nData bytes,
+** to changeset-group p.
+*/
+int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void *pData){
+ sqlite3_changeset_iter *pIter; /* Iterator opened on pData/nData */
+ int rc; /* Return code */
+
+ rc = sqlite3changeset_start(&pIter, nData, pData);
+ if( rc==SQLITE_OK ){
+ rc = sessionChangesetToHash(pIter, pGrp);
+ }
+ sqlite3changeset_finalize(pIter);
+ return rc;
+}
+
+/*
+** Obtain a buffer containing a changeset representing the concatenation
+** of all changesets added to the group so far.
+*/
+int sqlite3changegroup_output(
+ sqlite3_changegroup *pGrp,
+ int *pnData,
+ void **ppData
+){
+ return sessionChangegroupOutput(pGrp, 0, 0, pnData, ppData);
+}
+
+/*
+** Streaming versions of changegroup_add().
+*/
+int sqlite3changegroup_add_strm(
+ sqlite3_changegroup *pGrp,
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+){
+ sqlite3_changeset_iter *pIter; /* Iterator opened on pData/nData */
+ int rc; /* Return code */
+
+ rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
+ if( rc==SQLITE_OK ){
+ rc = sessionChangesetToHash(pIter, pGrp);
+ }
+ sqlite3changeset_finalize(pIter);
+ return rc;
+}
+
+/*
+** Streaming versions of changegroup_output().
+*/
+int sqlite3changegroup_output_strm(
+ sqlite3_changegroup *pGrp,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+){
+ return sessionChangegroupOutput(pGrp, xOutput, pOut, 0, 0);
+}
+
+/*
+** Delete a changegroup object.
+*/
+void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
+ if( pGrp ){
+ sessionDeleteTable(pGrp->pList);
+ sqlite3_free(pGrp);
+ }
+}
+
/*
** Combine two changesets together.
*/
int *pnOut, /* OUT: Number of bytes in output changeset */
void **ppOut /* OUT: changeset (left <concat> right) */
){
- sqlite3_changeset_iter *pIter1 = 0;
- sqlite3_changeset_iter *pIter2 = 0;
+ sqlite3_changegroup *pGrp;
int rc;
- *pnOut = 0;
- *ppOut = 0;
- rc = sqlite3changeset_start(&pIter1, nLeft, pLeft);
+ rc = sqlite3changegroup_new(&pGrp);
if( rc==SQLITE_OK ){
- rc = sqlite3changeset_start(&pIter2, nRight, pRight);
+ rc = sqlite3changegroup_add(pGrp, nLeft, pLeft);
}
if( rc==SQLITE_OK ){
- rc = sessionChangesetConcat(pIter1, pIter2, 0, 0, pnOut, ppOut);
+ rc = sqlite3changegroup_add(pGrp, nRight, pRight);
}
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_output(pGrp, pnOut, ppOut);
+ }
+ sqlite3changegroup_delete(pGrp);
- sqlite3changeset_finalize(pIter1);
- sqlite3changeset_finalize(pIter2);
return rc;
}
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
){
- sqlite3_changeset_iter *pIter1 = 0;
- sqlite3_changeset_iter *pIter2 = 0;
+ sqlite3_changegroup *pGrp;
int rc;
- rc = sqlite3changeset_start_strm(&pIter1, xInputA, pInA);
+ rc = sqlite3changegroup_new(&pGrp);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_add_strm(pGrp, xInputA, pInA);
+ }
if( rc==SQLITE_OK ){
- rc = sqlite3changeset_start_strm(&pIter2, xInputB, pInB);
+ rc = sqlite3changegroup_add_strm(pGrp, xInputB, pInB);
}
if( rc==SQLITE_OK ){
- rc = sessionChangesetConcat(pIter1, pIter2, xOutput, pOut, 0, 0);
+ rc = sqlite3changegroup_output_strm(pGrp, xOutput, pOut);
}
+ sqlite3changegroup_delete(pGrp);
- sqlite3changeset_finalize(pIter1);
- sqlite3changeset_finalize(pIter2);
return rc;
}
** single changeset. The result is a changeset equivalent to applying
** changeset A followed by changeset B.
**
-** Rows are identified by the values in their PRIMARY KEY columns. A change
-** in changeset A is considered to apply to the same row as a change in
-** changeset B if the two rows have the same primary key.
+** This function combines the two input changesets using an
+** sqlite3_changegroup object. Calling it produces similar results as the
+** following code fragment:
+**
+** sqlite3_changegroup *pGrp;
+** rc = sqlite3_changegroup_new(&pGrp);
+** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA);
+** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nB, pB);
+** if( rc==SQLITE_OK ){
+** rc = sqlite3changegroup_output(pGrp, pnOut, ppOut);
+** }else{
+** *ppOut = 0;
+** *pnOut = 0;
+** }
**
-** Changes to rows that appear only in changeset A or B are copied into the
-** output changeset. Or, if both changeset A and B contain a change that
-** applies to a single row, the output depends on the type of each change,
-** as follows:
+** Refer to the sqlite3_changegroup documentation below for details.
+*/
+int sqlite3changeset_concat(
+ int nA, /* Number of bytes in buffer pA */
+ void *pA, /* Pointer to buffer containing changeset A */
+ int nB, /* Number of bytes in buffer pB */
+ void *pB, /* Pointer to buffer containing changeset B */
+ int *pnOut, /* OUT: Number of bytes in output changeset */
+ void **ppOut /* OUT: Buffer containing output changeset */
+);
+
+
+/*
+** Changegroup handle.
+*/
+typedef struct sqlite3_changegroup sqlite3_changegroup;
+
+/*
+** CAPI3REF: Combine two or more changesets into a single changeset.
+**
+** An sqlite3_changegroup object is used to combine two or more changesets
+** (or patchsets) into a single changeset (or patchset). A single changegroup
+** object may combine changesets or patchsets, but not both. The output is
+** always in the same format as the input.
+**
+** If successful, this function returns SQLITE_OK and populates (*pp) with
+** a pointer to a new sqlite3_changegroup object before returning. The caller
+** should eventually free the returned object using a call to
+** sqlite3changegroup_delete(). If an error occurs, an SQLite error code
+** (i.e. SQLITE_NOMEM) is returned and *pp is set to NULL.
+**
+** The usual usage pattern for an sqlite3_changegroup object is as follows:
+**
+** <ul>
+** <li> It is created using a call to sqlite3changegroup_new().
+**
+** <li> Zero or more changesets (or patchsets) are added to the object
+** by calling sqlite3changegroup_add().
+**
+** <li> The result of combining all input changesets together is obtained
+** by the application via a call to sqlite3changegroup_output().
+**
+** <li> The object is deleted using a call to sqlite3changegroup_delete().
+** </ul>
+**
+** Any number of calls to add() and output() may be made between the calls to
+** new() and delete(), and in any order.
+**
+** As well as the regular sqlite3changegroup_add() and
+** sqlite3changegroup_output() functions, also available are the streaming
+** versions sqlite3changegroup_add_strm() and sqlite3changegroup_output_strm().
+*/
+int sqlite3changegroup_new(sqlite3_changegroup **pp);
+
+/*
+** Add all changes within the changeset (or patchset) in buffer pData (size
+** nData bytes) to the changegroup.
+**
+** If the buffer contains a patchset, then all prior calls to this function
+** on the same changegroup object must also have specified patchsets. Or, if
+** the buffer contains a changeset, so must have the earlier calls to this
+** function. Otherwise, SQLITE_ERROR is returned and no changes are added
+** to the changegroup.
+**
+** Rows within the changeset and changegroup are identified by the values in
+** their PRIMARY KEY columns. A change in the changeset is considered to
+** apply to the same row as a change already present in the changegroup if
+** the two rows have the same primary key.
+**
+** Changes to rows that that do not already appear in the changegroup are
+** simply copied into it. Or, if both the new changeset and the changegroup
+** contain changes that apply to a single row, the final contents of the
+** changegroup depends on the type of each change, as follows:
**
** <table border=1 style="margin-left:8ex;margin-right:8ex">
-** <tr><th style="white-space:pre">Change A </th>
-** <th style="white-space:pre">Change B </th>
+** <tr><th style="white-space:pre">Existing Change </th>
+** <th style="white-space:pre">New Change </th>
** <th>Output Change
** <tr><td>INSERT <td>INSERT <td>
-** Change A is copied into the output changeset. Change B is discarded.
-** This case does not occur if changeset B is recorded immediately after
-** changeset A.
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
** <tr><td>INSERT <td>UPDATE <td>
-** An INSERT change is copied into the output changeset. The values in
-** the INSERT change are as if the row was inserted by change A and then
-** updated according to change B.
+** The INSERT change remains in the changegroup. The values in the
+** INSERT change are modified as if the row was inserted by the
+** existing change and then updated according to the new change.
** <tr><td>INSERT <td>DELETE <td>
-** No change at all is copied into the output changeset.
+** The existing INSERT is removed from the changegroup. The DELETE is
+** not added.
** <tr><td>UPDATE <td>INSERT <td>
-** Change A is copied into the output changeset. Change B is discarded.
-** This case does not occur if changeset B is recorded immediately after
-** changeset A.
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
** <tr><td>UPDATE <td>UPDATE <td>
-** A single UPDATE is copied into the output changeset. The accompanying
-** values are as if the row was updated once by change A and then again
-** by change B.
+** The existing UPDATE remains within the changegroup. It is amended
+** so that the accompanying values are as if the row was updated once
+** by the existing change and then again by the new change.
** <tr><td>UPDATE <td>DELETE <td>
-** A single DELETE is copied into the output changeset.
+** The existing UPDATE is replaced by the new DELETE within the
+** changegroup.
** <tr><td>DELETE <td>INSERT <td>
-** If one or more of the column values in the row inserted by change
-** B differ from those in the row deleted by change A, an UPDATE
-** change is added to the output changeset. Otherwise, if the inserted
-** row is exactly the same as the deleted row, no change is added to
-** the output changeset.
+** If one or more of the column values in the row inserted by the
+** new change differ from those in the row deleted by the existing
+** change, the existing DELETE is replaced by an UPDATE within the
+** changegroup. Otherwise, if the inserted row is exactly the same
+** as the deleted row, the existing DELETE is simply discarded.
** <tr><td>DELETE <td>UPDATE <td>
-** Change A is copied into the output changeset. Change B is discarded.
-** This case does not occur if changeset B is recorded immediately after
-** changeset A.
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
** <tr><td>DELETE <td>DELETE <td>
-** Change A is copied into the output changeset. Change B is discarded.
-** This case does not occur if changeset B is recorded immediately after
-** changeset A.
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
** </table>
**
-** If the two changesets contain changes to the same table, then the number
-** of columns and the position of the primary key columns for the table must
-** be the same in each changeset. If this is not the case, attempting to
-** concatenate the two changesets together fails and this function returns
-** SQLITE_SCHEMA. If either of the two input changesets appear to be corrupt,
-** and the corruption is detected, SQLITE_CORRUPT is returned. Or, if an
-** out-of-memory condition occurs during processing, this function returns
-** SQLITE_NOMEM.
+** If the new changeset contains changes to a table that is already present
+** in the changegroup, then the number of columns and the position of the
+** primary key columns for the table must be consistent. If this is not the
+** case, this function fails with SQLITE_SCHEMA. If the input changeset
+** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
+** returned. Or, if an out-of-memory condition occurs during processing, this
+** function returns SQLITE_NOMEM. In all cases, if an error occurs the
+** final contents of the changegroup is undefined.
**
-** If none of the above errors occur, SQLITE_OK is returned and *ppOut set
-** to point to a buffer containing the output changeset. It is the
-** responsibility of the caller to eventually call sqlite3_free() on *ppOut
-** to release memory allocated for the buffer. *pnOut is set to the number
-** of bytes in the output changeset. If an error does occur, both *ppOut and
-** *pnOut are set to zero before returning.
+** If no error occurs, SQLITE_OK is returned.
*/
-int sqlite3changeset_concat(
- int nA, /* Number of bytes in buffer pA */
- void *pA, /* Pointer to buffer containing changeset A */
- int nB, /* Number of bytes in buffer pB */
- void *pB, /* Pointer to buffer containing changeset B */
- int *pnOut, /* OUT: Number of bytes in output changeset */
- void **ppOut /* OUT: Buffer containing output changeset */
+int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
+
+/*
+** Obtain a buffer containing a changeset (or patchset) representing the
+** current contents of the changegroup. If the inputs to the changegroup
+** were themselves changesets, the output is a changeset. Or, if the
+** inputs were patchsets, the output is also a patchset.
+**
+** If an error occurs, an SQLite error code is returned and the output
+** variables (*pnData) and (*ppData) are set to 0. Otherwise, SQLITE_OK
+** is returned and the output variables are set to the size of and a
+** pointer to the output buffer, respectively. In this case it is the
+** responsibility of the caller to eventually free the buffer using a
+** call to sqlite3_free().
+*/
+int sqlite3changegroup_output(
+ sqlite3_changegroup*,
+ int *pnData, /* OUT: Size of output buffer in bytes */
+ void **ppData /* OUT: Pointer to output buffer */
);
+/*
+** Delete a changegroup object.
+*/
+void sqlite3changegroup_delete(sqlite3_changegroup*);
+
/*
** CAPI3REF: Apply A Changeset To A Database
**
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
);
+int sqlite3changegroup_add_strm(sqlite3_changegroup*,
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+);
+int sqlite3changegroup_output_strm(sqlite3_changegroup*,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
/*
-C Add\sthe\s"finish_test"\scommand\sto\sthe\send\sof\snew\stest\sscript\ssessionE.test.
-D 2015-06-02T09:20:46.301
+C Add\sthe\ssqlite3changegroup_xxx()\sAPIs\sto\sthe\ssessions\smodule.\sFor\scombining\smultiple\schangesets\sor\spatchsets.
+D 2015-06-11T17:26:10.939
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 58c16cc8cd876ed112902e70cf33d33f3270b5aa
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F ext/session/sessionE.test e60a238c47f0feb3bb707e7f35e22be09c7e8f26
F ext/session/session_common.tcl f4b7b59c617edf0c9b00d94cd93498d225d43837
F ext/session/sessionfault.test bef044d0952c0d62c31c8d2400be72c8684545cc
-F ext/session/sqlite3session.c d630293057fcf4274451edec24c2745953ca042c
-F ext/session/sqlite3session.h 8e86f9eec3ed71f1f30eefbe810cbe5bc10b5aa9
+F ext/session/sqlite3session.c 78e5b310cd6e655e9166f40dc8a319c0c866cfdc
+F ext/session/sqlite3session.h 772cffe38f11bd62f434eabf080157eabf3d553e
F ext/session/test_session.c 187bd344c5ae9d5be85e22ef7c3010f0c17307ce
F ext/userauth/sqlite3userauth.h 19cb6f0e31316d0ee4afdfb7a85ef9da3333a220
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P bdaf9575cd9ebb33dc5da4062a84bca79e7b0fec
-R dbe1b57e21e93824d104fb3b930d73e6
+P fb3914070791c84b5f323b7359ac845246d8a844
+R 8257ada0208a724be61180fd75fdecb0
U dan
-Z cd8fea501989560b39f7780ac3d3b3f9
+Z fef5e9795fb303b4eab93bd557fcafaf
-fb3914070791c84b5f323b7359ac845246d8a844
\ No newline at end of file
+0c1a901cd60e557fc676b97625243163dfe9be9d
\ No newline at end of file