From 16228167c24b757214d2f8485d8b79c1bc0dc16f Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 27 Sep 2014 12:26:18 +0000 Subject: [PATCH] Improve sessions module documentation and comments. Fix some other code issues. FossilOrigin-Name: bfc8bd80f8b225cebc66478448510ce84223ae7d --- ext/session/sqlite3session.c | 48 ++++--- ext/session/sqlite3session.h | 237 ++++++++++++++++++++--------------- manifest | 14 +-- manifest.uuid | 2 +- 4 files changed, 171 insertions(+), 130 deletions(-) diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index a0e0697ee3..eb1ff1e774 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -49,10 +49,10 @@ struct SessionBuffer { }; /* -** 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 */ @@ -2173,6 +2173,10 @@ static int sessionValueSetStr( 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); @@ -2299,12 +2303,16 @@ static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){ /* ** 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; @@ -2335,6 +2343,15 @@ static int sessionChangesetBufferRecord( ** + 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; @@ -3775,7 +3792,7 @@ static int sessionChangeMerge( ** 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 */ ){ @@ -3794,15 +3811,6 @@ static int sessionAddChangeset( 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 */ @@ -3899,9 +3907,9 @@ int sessionChangesetConcat( 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; diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index ad58c0d772..0dd03067ed 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -274,30 +274,6 @@ int sqlite3session_changeset( ); -/* -** 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 ** @@ -327,15 +303,6 @@ int sqlite3session_patchset( 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. ** @@ -393,29 +360,6 @@ int sqlite3changeset_start( ); -/* -** 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 ** @@ -670,16 +614,6 @@ int sqlite3changeset_invert( 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 ** @@ -761,18 +695,6 @@ int sqlite3changeset_concat( 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 ** @@ -925,30 +847,6 @@ int sqlite3changeset_apply( 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 ** @@ -1045,6 +943,141 @@ int sqlite3changeset_apply_str( #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: +** +** +** +**
Streaming functionNon-streaming equivalent
sqlite3changeset_apply_str[sqlite3changeset_apply] +**
sqlite3changeset_concat_str[sqlite3changeset_concat] +**
sqlite3changeset_invert_str[sqlite3changeset_invert] +**
sqlite3changeset_start_str[sqlite3changeset_start] +**
sqlite3session_changeset_str[sqlite3session_changeset] +**
sqlite3session_patchset_str[sqlite3session_patchset] +**
+** +** 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 +** +**
+**        int nChangeset,
+**        void *pChangeset,
+**  
+** +** Is replaced by: +** +**
+**        int (*xInput)(void *pIn, void *pData, int *pnData),
+**        void *pIn,
+**  
+** +** 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: +** +**
+**        int *pnChangeset,
+**        void **ppChangeset,
+**  
+** +** Is replaced by: +** +**
+**        int (*xOutput)(void *pOut, const void *pData, int nData),
+**        void *pOut
+**  
+** +** 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++. */ diff --git a/manifest b/manifest index 07a4e34a3b..258fb598ba 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swith\sconcatenating\spatchsets\scontaining\sDELETE\sand\sINSERT\soperations\son\sthe\ssame\srow. -D 2014-09-26T10:52:21.193 +C Improve\ssessions\smodule\sdocumentation\sand\scomments.\sFix\ssome\sother\scode\sissues. +D 2014-09-27T12:26:18.848 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in dd5f245aa8c741bc65845747203c8ce2f3fb6c83 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -158,8 +158,8 @@ F ext/session/sessionA.test eb05c13e4ef1ca8046a3a6dbf2d5f6f5b04a11d4 F ext/session/sessionB.test d4ac901b43d4922a17dff08bbaa2f5354487ce4d F ext/session/session_common.tcl 1539d8973b2aea0025c133eb0cc4c89fcef541a5 F ext/session/sessionfault.test e7965159a73d385c1a4af12d82c3a039ebdd71a6 -F ext/session/sqlite3session.c ade9fa2f7341b822dddaa865115ec964a030df94 -F ext/session/sqlite3session.h 04529352750006b32811384db64eb1b6e5c3cd80 +F ext/session/sqlite3session.c 9ce77f4752cd5d8982e7b64f665ae66754dda723 +F ext/session/sqlite3session.h b57009fb88835cc4684376bd3eae0d6ab364968a F ext/session/test_session.c 194083ee1f0f6f38404f662fe9b50849abd3b7ee F ext/userauth/sqlite3userauth.h 19cb6f0e31316d0ee4afdfb7a85ef9da3333a220 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 @@ -1216,7 +1216,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 88eb6656bdb047a104837a2e15e7fe18c0a7a159 -R 81a0c7112c65d31bc1808253eb2f372f +P 4d8537eafb40e3687abc057ba26a1f7014f2c2d9 +R c2c5a501f3f294cbca1bcb47eb806551 U dan -Z a169087f5f6ea34184b5d5b4868830f7 +Z c7ab4f2784fbdfc318b000ddf7671e24 diff --git a/manifest.uuid b/manifest.uuid index c388e5763a..31da46d10b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4d8537eafb40e3687abc057ba26a1f7014f2c2d9 \ No newline at end of file +bfc8bd80f8b225cebc66478448510ce84223ae7d \ No newline at end of file