};
/*
-** An object of this type is used internally as an abstraction for the
-** input data read by changeset iterators. Input data may be supplied
-** either as a single large buffer (sqlite3changeset_start()) or using
-** a stream function (sqlite3changeset_start_str()).
+** An object of this type is used internally as an abstraction for
+** input data. Input data may be supplied either as a single large buffer
+** (e.g. sqlite3changeset_start()) or using a stream function (e.g.
+** sqlite3changeset_start_str()).
*/
struct SessionInput {
int iNext; /* Offset in aData[] of next change */
int nData, /* Size of buffer aData[] in bytes */
u8 enc /* String encoding (0 for blobs) */
){
+ /* In theory this code could just pass SQLITE_TRANSIENT as the final
+ ** argument to sqlite3ValueSetStr() and have the copy created
+ ** automatically. But doing so makes it difficult to detect any OOM
+ ** error. Hence the code to create the copy externally. */
u8 *aCopy = sqlite3_malloc(nData);
if( aCopy==0 ) return SQLITE_NOMEM;
memcpy(aCopy, aData, nData);
/*
** The input pointer currently points to the first byte of the first field
** of a record consisting of nCol columns. This function ensures the entire
-** record is buffered.
+** record is buffered. It does not move the input pointer.
+**
+** If successful, SQLITE_OK is returned and *pnByte is set to the size of
+** the record in bytes. Otherwise, an SQLite error code is returned. The
+** final value of *pnByte is undefined in this case.
*/
static int sessionChangesetBufferRecord(
- SessionInput *pIn,
- int nCol,
- int *pnByte
+ SessionInput *pIn, /* Input data */
+ int nCol, /* Number of columns in record */
+ int *pnByte /* OUT: Size of record in bytes */
){
int rc = SQLITE_OK;
int nByte = 0;
** + number of columns in table (varint)
** + array of PK flags (1 byte per column),
** + table name (nul terminated).
+**
+** This function decodes the table-header and populates the p->nCol,
+** p->zTab and p->abPK[] variables accordingly. The p->apValue[] array is
+** also allocated or resized according to the new value of p->nCol. The
+** input pointer is left pointing to the byte following the table header.
+**
+** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code
+** is returned and the final values of the various fields enumerated above
+** are undefined.
*/
static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
int rc;
** Add all changes in the changeset passed via the first two arguments to
** hash tables.
*/
-static int sessionAddChangeset(
+static int sessionChangesetToHash(
sqlite3_changeset_iter *pIter, /* Iterator to read from */
SessionTable **ppTabList /* IN/OUT: List of table objects */
){
SessionChange *pExist = 0;
SessionChange **pp;
-#if 0
- assert( bPatchset==0 || bPatchset==1 );
- assert( pIter->bPatchset==0 || pIter->bPatchset==1 );
- if( pIter->bPatchset!=bPatchset ){
- rc = SQLITE_ERROR;
- break;
- }
-#endif
-
sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect);
if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){
/* Search the list for a matching table */
assert( xOutput==0 || (ppOut==0 && pnOut==0) );
- rc = sessionAddChangeset(pLeft, &pList);
+ rc = sessionChangesetToHash(pLeft, &pList);
if( rc==SQLITE_OK ){
- rc = sessionAddChangeset(pRight, &pList);
+ rc = sessionChangesetToHash(pRight, &pList);
}
bPatch = pLeft->bPatchset || pRight->bPatchset;
);
-/*
-** This function is similar to sqlite3session_changeset(), except that instead
-** of storing the output changeset in a buffer obtained from sqlite3_malloc()
-** it invokes the supplied xOutput() callback zero or more times to stream the
-** changeset to the application. This is useful in order to avoid large memory
-** allocations when working with very large changesets.
-**
-** The first parameter passed to each call to the xOutput callback is a copy
-** of the pOut parameter passed to this function. The following two parameters
-** are a pointer to the buffer containing the next chunk of the output changeset
-** and the size of that buffer in bytes.
-**
-** If the data is successfully processed by the xOutput callback, it should
-** return SQLITE_OK. Or, if an error occurs, some other SQLite error code. In
-** this case the sqlite3session_changeset_str() call is abandoned immediately
-** and returns a copy of the xOutput return code.
-*/
-int sqlite3session_changeset_str(
- sqlite3_session *pSession,
- int (*xOutput)(void *pOut, const void *pData, int nData),
- void *pOut
-);
-
-
/*
** CAPI3REF: Generate A Patchset From A Session Object
**
void **ppPatchset /* OUT: Buffer containing changeset */
);
-/*
-** Streaming version of sqlite3session_patchset().
-*/
-int sqlite3session_patchset_str(
- sqlite3_session *pSession,
- int (*xOutput)(void *pOut, const void *pData, int nData),
- void *pOut
-);
-
/*
** CAPI3REF: Test if a changeset has recorded any changes.
**
);
-/*
-** This function is similar to sqlite3changeset_start(), except that instead
-** of reading data from a single buffer, it requests it one chunk at a time
-** from the application by invoking the supplied xInput() callback. The xInput()
-** callback may be invoked at any time during the lifetime of the iterator.
-**
-** Each time the xInput callback is invoked, the first argument passed is a
-** copy of the third parameter passed to this function. The second argument,
-** pData, points to a buffer (*pnData) bytes in size. Assuming no error occurs
-** the xInput method should copy up to (*pnData) bytes of data into the buffer
-** and set (*pnData) to the actual number of bytes copied before returning
-** SQLITE_OK. If the input is completely exhausted, (*pnData) should be set
-** to zero to indicate this. Or, if an error occurs, an SQLite error code
-** should be returned. In this case the iterator is put into an error state and
-** all subsequent calls to iterator methods return a copy of the xInput error
-** code.
-*/
-int sqlite3changeset_start_str(
- sqlite3_changeset_iter **pp,
- int (*xInput)(void *pIn, void *pData, int *pnData),
- void *pIn
-);
-
/*
** CAPI3REF: Advance A Changeset Iterator
**
int *pnOut, void **ppOut /* OUT: Inverse of input */
);
-/*
-** Streaming version of sqlite3changeset_invert().
-*/
-int sqlite3changeset_invert_str(
- int (*xInput)(void *pIn, void *pData, int *pnData),
- void *pIn,
- int (*xOutput)(void *pOut, const void *pData, int nData),
- void *pOut
-);
-
/*
** CAPI3REF: Concatenate Two Changeset Objects
**
void **ppOut /* OUT: Buffer containing output changeset */
);
-/*
-** Streaming verson of sqlite3changeset_concat().
-*/
-int sqlite3changeset_concat_str(
- int (*xInputA)(void *pIn, void *pData, int *pnData),
- void *pInA,
- int (*xInputB)(void *pIn, void *pData, int *pnData),
- void *pInB,
- int (*xOutput)(void *pOut, const void *pData, int nData),
- void *pOut
-);
-
/*
** CAPI3REF: Apply A Changeset To A Database
**
void *pCtx /* First argument passed to xConflict */
);
-/*
-** This function is similar to sqlite3changeset_apply(), except that instead
-** of reading data from a single buffer, it requests it one chunk at a time
-** from the application by invoking the supplied xInput() callback.
-**
-** See the documentation for sqlite3changeset_start_str() for a description
-** of how the xInput callback should be implemented.
-*/
-int sqlite3changeset_apply_str(
- sqlite3 *db, /* Apply change to "main" db of this handle */
- int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
- void *pIn, /* First arg for xInput */
- int(*xFilter)(
- void *pCtx, /* Copy of sixth arg to _apply() */
- const char *zTab /* Table name */
- ),
- int(*xConflict)(
- void *pCtx, /* Copy of sixth arg to _apply() */
- int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
- sqlite3_changeset_iter *p /* Handle describing change and conflict */
- ),
- void *pCtx /* First argument passed to xConflict */
-);
-
/*
** CAPI3REF: Constants Passed To The Conflict Handler
**
#define SQLITE_CHANGESET_REPLACE 1
#define SQLITE_CHANGESET_ABORT 2
+/*
+** CAPI3REF: Streaming Versions of API functions.
+**
+** The six streaming API xxx_str() functions serve similar purposes to the
+** corresponding non-streaming API functions:
+**
+** <table border=1 style="margin-left:8ex;margin-right:8ex">
+** <tr><th>Streaming function<th>Non-streaming equivalent</th>
+** <tr><td>sqlite3changeset_apply_str<td>[sqlite3changeset_apply]
+** <tr><td>sqlite3changeset_concat_str<td>[sqlite3changeset_concat]
+** <tr><td>sqlite3changeset_invert_str<td>[sqlite3changeset_invert]
+** <tr><td>sqlite3changeset_start_str<td>[sqlite3changeset_start]
+** <tr><td>sqlite3session_changeset_str<td>[sqlite3session_changeset]
+** <tr><td>sqlite3session_patchset_str<td>[sqlite3session_patchset]
+** </table>
+**
+** Non-streaming functions that accept changesets (or patchsets) as input
+** require that the entire changeset be stored in a single buffer in memory.
+** Similarly, those that return a changeset or patchset do so by returning
+** a pointer to a single large buffer allocated using sqlite3_malloc().
+** Normally this is convenient. However, if an application running in a
+** low-memory environment is required to handle very large changesets, the
+** large contiguous memory allocations required can become onerous.
+**
+** In order to avoid this problem, instead of a single large buffer, input
+** is passed to a streaming API functions by way of a callback function that
+** the sessions module invokes to incrementally request input data as it is
+** required. In all cases, a pair of API function parameters such as
+**
+** <pre>
+** int nChangeset,
+** void *pChangeset,
+** </pre>
+**
+** Is replaced by:
+**
+** <pre>
+** int (*xInput)(void *pIn, void *pData, int *pnData),
+** void *pIn,
+** </pre>
+**
+** Each time the xInput callback is invoked by the sessions module, the first
+** argument passed is a copy of the supplied pIn context pointer. The second
+** argument, pData, points to a buffer (*pnData) bytes in size. Assuming no
+** error occurs the xInput method should copy up to (*pnData) bytes of data
+** into the buffer and set (*pnData) to the actual number of bytes copied
+** before returning SQLITE_OK. If the input is completely exhausted, (*pnData)
+** should be set to zero to indicate this. Or, if an error occurs, an SQLite
+** error code should be returned. In all cases, if an xInput callback returns
+** an error, all processing is abandoned and the streaming API function
+** returns a copy of the error code to the caller.
+**
+** In the case of sqlite3changeset_start_str(), the xInput callback may be
+** invoked by the sessions module at any point during the lifetime of the
+** iterator. If such an xInput callback returns an error, the iterator enters
+** an error state, whereby all subsequent calls to iterator functions
+** immediately fail with the same error code as returned by xInput.
+**
+** Similarly, streaming API functions that return changesets (or patchsets)
+** return them in chunks by way of a callback function instead of via a
+** pointer to a single large buffer. In this case, a pair of parameters such
+** as:
+**
+** <pre>
+** int *pnChangeset,
+** void **ppChangeset,
+** </pre>
+**
+** Is replaced by:
+**
+** <pre>
+** int (*xOutput)(void *pOut, const void *pData, int nData),
+** void *pOut
+** </pre>
+**
+** The xOutput callback is invoked zero or more times to return data to
+** the application. The first parameter passed to each call is a copy of the
+** pOut pointer supplied by the application. The second parameter, pData,
+** points to a buffer nData bytes in size containing the chunk of output
+** data being returned. If the xOutput callback successfully processes the
+** supplied data, it should return SQLITE_OK to indicate success. Otherwise,
+** it should return some other SQLite error code. In this case processing
+** is immediately abandoned and the streaming API function returns a copy
+** of the xOutput error code to the application.
+**
+** The sessions module never invokes an xOutput callback with the third
+** parameter set to a value less than or equal to zero. Other than this,
+** no guarantees are made as to the size of the chunks of data returned.
+*/
+int sqlite3changeset_apply_str(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
+ void *pIn, /* First arg for xInput */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+);
+int sqlite3changeset_concat_str(
+ int (*xInputA)(void *pIn, void *pData, int *pnData),
+ void *pInA,
+ int (*xInputB)(void *pIn, void *pData, int *pnData),
+ void *pInB,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3changeset_invert_str(
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3changeset_start_str(
+ sqlite3_changeset_iter **pp,
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+);
+int sqlite3session_changeset_str(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3session_patchset_str(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+
+
/*
** Make sure we can call this stuff from C++.
*/