14fccf43aSdan 
24fccf43aSdan #ifdef SQLITE_ENABLE_SESSION
34fccf43aSdan 
44fccf43aSdan #include "sqlite3session.h"
54fccf43aSdan #include <assert.h>
64fccf43aSdan #include <string.h>
74fccf43aSdan 
84fccf43aSdan #include "sqliteInt.h"
94fccf43aSdan #include "vdbeInt.h"
104fccf43aSdan 
114fccf43aSdan typedef struct SessionTable SessionTable;
124fccf43aSdan typedef struct SessionChange SessionChange;
13296c7658Sdan typedef struct SessionBuffer SessionBuffer;
144fccf43aSdan 
15296c7658Sdan /*
16296c7658Sdan ** Session handle structure.
17296c7658Sdan */
184fccf43aSdan struct sqlite3_session {
194fccf43aSdan   sqlite3 *db;                    /* Database handle session is attached to */
204fccf43aSdan   char *zDb;                      /* Name of database session is attached to */
21296c7658Sdan   int bEnable;                    /* True if currently recording */
22*ff4d0f41Sdan   int bAutoAttach;                /* True to auto-attach tables */
234fccf43aSdan   int rc;                         /* Non-zero if an error has occurred */
244fccf43aSdan   sqlite3_session *pNext;         /* Next session object on same db. */
254fccf43aSdan   SessionTable *pTable;           /* List of attached tables */
264fccf43aSdan };
274fccf43aSdan 
284fccf43aSdan /*
29296c7658Sdan ** Structure for changeset iterators.
30296c7658Sdan */
31296c7658Sdan struct sqlite3_changeset_iter {
32296c7658Sdan   u8 *aChangeset;                 /* Pointer to buffer containing changeset */
33296c7658Sdan   int nChangeset;                 /* Number of bytes in aChangeset */
34296c7658Sdan   u8 *pNext;                      /* Pointer to next change within aChangeset */
35296c7658Sdan   int rc;                         /* Iterator error code */
36296c7658Sdan   sqlite3_stmt *pConflict;        /* Points to conflicting row, if any */
37296c7658Sdan   char *zTab;                     /* Current table */
38296c7658Sdan   int nCol;                       /* Number of columns in zTab */
39296c7658Sdan   int op;                         /* Current operation */
40296c7658Sdan   sqlite3_value **apValue;        /* old.* and new.* values */
41296c7658Sdan };
42296c7658Sdan 
43296c7658Sdan /*
444fccf43aSdan ** Each session object maintains a set of the following structures, one
454fccf43aSdan ** for each table the session object is monitoring. The structures are
464fccf43aSdan ** stored in a linked list starting at sqlite3_session.pTable.
474fccf43aSdan **
484fccf43aSdan ** The keys of the SessionTable.aChange[] hash table are all rows that have
494fccf43aSdan ** been modified in any way since the session object was attached to the
504fccf43aSdan ** table.
514fccf43aSdan **
524fccf43aSdan ** The data associated with each hash-table entry is a structure containing
534fccf43aSdan ** a subset of the initial values that the modified row contained at the
544fccf43aSdan ** start of the session. Or no initial values if the row was inserted.
554fccf43aSdan */
564fccf43aSdan struct SessionTable {
574fccf43aSdan   SessionTable *pNext;
584fccf43aSdan   char *zName;                    /* Local name of table */
594fccf43aSdan   int nCol;                       /* Number of columns in table zName */
60e8d5648eSdan   const char **azCol;             /* Column names */
61e8d5648eSdan   u8 *abPK;                       /* Array of primary key flags */
62296c7658Sdan   int nEntry;                     /* Total number of entries in hash table */
634fccf43aSdan   int nChange;                    /* Size of apChange[] array */
644fccf43aSdan   SessionChange **apChange;       /* Hash table buckets */
654fccf43aSdan };
664fccf43aSdan 
674fccf43aSdan /*
684fccf43aSdan ** RECORD FORMAT:
694fccf43aSdan **
704fccf43aSdan ** The following record format is similar to (but not compatible with) that
714fccf43aSdan ** used in SQLite database files. This format is used as part of the
724fccf43aSdan ** change-set binary format, and so must be architecture independent.
734fccf43aSdan **
744fccf43aSdan ** Unlike the SQLite database record format, each field is self-contained -
754fccf43aSdan ** there is no separation of header and data. Each field begins with a
764fccf43aSdan ** single byte describing its type, as follows:
774fccf43aSdan **
784fccf43aSdan **       0x00: Undefined value.
794fccf43aSdan **       0x01: Integer value.
804fccf43aSdan **       0x02: Real value.
814fccf43aSdan **       0x03: Text value.
824fccf43aSdan **       0x04: Blob value.
834fccf43aSdan **       0x05: SQL NULL value.
844fccf43aSdan **
854fccf43aSdan ** Note that the above match the definitions of SQLITE_INTEGER, SQLITE_TEXT
864fccf43aSdan ** and so on in sqlite3.h. For undefined and NULL values, the field consists
874fccf43aSdan ** only of the single type byte. For other types of values, the type byte
884fccf43aSdan ** is followed by:
894fccf43aSdan **
904fccf43aSdan **   Text values:
914fccf43aSdan **     A varint containing the number of bytes in the value (encoded using
924fccf43aSdan **     UTF-8). Followed by a buffer containing the UTF-8 representation
934fccf43aSdan **     of the text value. There is no nul terminator.
944fccf43aSdan **
954fccf43aSdan **   Blob values:
964fccf43aSdan **     A varint containing the number of bytes in the value, followed by
974fccf43aSdan **     a buffer containing the value itself.
984fccf43aSdan **
994fccf43aSdan **   Integer values:
1004fccf43aSdan **     An 8-byte big-endian integer value.
1014fccf43aSdan **
1024fccf43aSdan **   Real values:
1034fccf43aSdan **     An 8-byte big-endian IEEE 754-2008 real value.
1044fccf43aSdan **
1054fccf43aSdan ** Varint values are encoded in the same way as varints in the SQLite
1064fccf43aSdan ** record format.
1074fccf43aSdan **
1084fccf43aSdan ** CHANGESET FORMAT:
1094fccf43aSdan **
1104fccf43aSdan ** A changeset is a collection of DELETE, UPDATE and INSERT operations on
1114fccf43aSdan ** one or more tables. Operations on a single table are grouped together,
1124fccf43aSdan ** but may occur in any order (i.e. deletes, updates and inserts are all
1134fccf43aSdan ** mixed together).
1144fccf43aSdan **
1154fccf43aSdan ** Each group of changes begins with a table header:
1164fccf43aSdan **
1174fccf43aSdan **   1 byte: Constant 0x54 (capital 'T')
1184fccf43aSdan **   Varint: Big-endian integer set to the number of columns in the table.
1194fccf43aSdan **   N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated.
1204fccf43aSdan **
1214fccf43aSdan ** Followed by one or more changes to the table.
1224fccf43aSdan **
1234fccf43aSdan **   1 byte: Either SQLITE_INSERT, UPDATE or DELETE.
1244fccf43aSdan **   old.* record: (delete and update only)
1254fccf43aSdan **   new.* record: (insert and update only)
1264fccf43aSdan */
1274fccf43aSdan 
1284fccf43aSdan /*
1294fccf43aSdan ** For each row modified during a session, there exists a single instance of
1304fccf43aSdan ** this structure stored in a SessionTable.aChange[] hash table.
1314fccf43aSdan */
1324fccf43aSdan struct SessionChange {
133e8d5648eSdan   int bInsert;                    /* True if row was inserted this session */
1344fccf43aSdan   int nRecord;                    /* Number of bytes in buffer aRecord[] */
1354fccf43aSdan   u8 *aRecord;                    /* Buffer containing old.* record */
1364fccf43aSdan   SessionChange *pNext;           /* For hash-table collisions */
1374fccf43aSdan };
1384fccf43aSdan 
139296c7658Sdan /*
140296c7658Sdan ** Instances of this structure are used to build strings or binary records.
141296c7658Sdan */
142296c7658Sdan struct SessionBuffer {
143296c7658Sdan   u8 *aBuf;                       /* Pointer to changeset buffer */
144296c7658Sdan   int nBuf;                       /* Size of buffer aBuf */
145296c7658Sdan   int nAlloc;                     /* Size of allocation containing aBuf */
146296c7658Sdan };
1474fccf43aSdan 
148296c7658Sdan /*
149296c7658Sdan ** Write a varint with value iVal into the buffer at aBuf. Return the
150296c7658Sdan ** number of bytes written.
151296c7658Sdan */
152296c7658Sdan static int sessionVarintPut(u8 *aBuf, int iVal){
153296c7658Sdan   return putVarint32(aBuf, iVal);
1544fccf43aSdan }
1554fccf43aSdan 
156296c7658Sdan /*
157296c7658Sdan ** Return the number of bytes required to store value iVal as a varint.
158296c7658Sdan */
159296c7658Sdan static int sessionVarintLen(int iVal){
160296c7658Sdan   return sqlite3VarintLen(iVal);
161296c7658Sdan }
162296c7658Sdan 
163296c7658Sdan /*
164296c7658Sdan ** Read a varint value from aBuf[] into *piVal. Return the number of
165296c7658Sdan ** bytes read.
166296c7658Sdan */
1674fccf43aSdan static int sessionVarintGet(u8 *aBuf, int *piVal){
168296c7658Sdan   return getVarint32(aBuf, *piVal);
1694fccf43aSdan }
1704fccf43aSdan 
171296c7658Sdan /*
172296c7658Sdan ** Read a 64-bit big-endian integer value from buffer aRec[]. Return
173296c7658Sdan ** the value read.
174296c7658Sdan */
1754fccf43aSdan static sqlite3_int64 sessionGetI64(u8 *aRec){
1764fccf43aSdan   return (((sqlite3_int64)aRec[0]) << 56)
1774fccf43aSdan        + (((sqlite3_int64)aRec[1]) << 48)
1784fccf43aSdan        + (((sqlite3_int64)aRec[2]) << 40)
1794fccf43aSdan        + (((sqlite3_int64)aRec[3]) << 32)
1804fccf43aSdan        + (((sqlite3_int64)aRec[4]) << 24)
1814fccf43aSdan        + (((sqlite3_int64)aRec[5]) << 16)
1824fccf43aSdan        + (((sqlite3_int64)aRec[6]) <<  8)
1834fccf43aSdan        + (((sqlite3_int64)aRec[7]) <<  0);
1844fccf43aSdan }
1854fccf43aSdan 
1864fccf43aSdan /*
187296c7658Sdan ** Write a 64-bit big-endian integer value to the buffer aBuf[].
188296c7658Sdan */
189296c7658Sdan static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){
190296c7658Sdan   aBuf[0] = (i>>56) & 0xFF;
191296c7658Sdan   aBuf[1] = (i>>48) & 0xFF;
192296c7658Sdan   aBuf[2] = (i>>40) & 0xFF;
193296c7658Sdan   aBuf[3] = (i>>32) & 0xFF;
194296c7658Sdan   aBuf[4] = (i>>24) & 0xFF;
195296c7658Sdan   aBuf[5] = (i>>16) & 0xFF;
196296c7658Sdan   aBuf[6] = (i>> 8) & 0xFF;
197296c7658Sdan   aBuf[7] = (i>> 0) & 0xFF;
198296c7658Sdan }
199296c7658Sdan 
200296c7658Sdan /*
2014fccf43aSdan ** This function is used to serialize the contents of value pValue (see
2024fccf43aSdan ** comment titled "RECORD FORMAT" above).
2034fccf43aSdan **
2044fccf43aSdan ** If it is non-NULL, the serialized form of the value is written to
2054fccf43aSdan ** buffer aBuf. *pnWrite is set to the number of bytes written before
2064fccf43aSdan ** returning. Or, if aBuf is NULL, the only thing this function does is
2074fccf43aSdan ** set *pnWrite.
2084fccf43aSdan **
2094fccf43aSdan ** If no error occurs, SQLITE_OK is returned. Or, if an OOM error occurs
2104fccf43aSdan ** within a call to sqlite3_value_text() (may fail if the db is utf-16))
2114fccf43aSdan ** SQLITE_NOMEM is returned.
2124fccf43aSdan */
2134fccf43aSdan static int sessionSerializeValue(
2144fccf43aSdan   u8 *aBuf,                       /* If non-NULL, write serialized value here */
2154fccf43aSdan   sqlite3_value *pValue,          /* Value to serialize */
2164fccf43aSdan   int *pnWrite                    /* IN/OUT: Increment by bytes written */
2174fccf43aSdan ){
218296c7658Sdan   int eType;                      /* Value type (SQLITE_NULL, TEXT etc.) */
219296c7658Sdan   int nByte;                      /* Size of serialized value in bytes */
2204fccf43aSdan 
2214fccf43aSdan   eType = sqlite3_value_type(pValue);
2224fccf43aSdan   if( aBuf ) aBuf[0] = eType;
2234fccf43aSdan 
2244fccf43aSdan   switch( eType ){
2254fccf43aSdan     case SQLITE_NULL:
2264fccf43aSdan       nByte = 1;
2274fccf43aSdan       break;
2284fccf43aSdan 
2294fccf43aSdan     case SQLITE_INTEGER:
2304fccf43aSdan     case SQLITE_FLOAT:
2314fccf43aSdan       if( aBuf ){
2324fccf43aSdan         /* TODO: SQLite does something special to deal with mixed-endian
2334fccf43aSdan         ** floating point values (e.g. ARM7). This code probably should
2344fccf43aSdan         ** too.  */
2354fccf43aSdan         u64 i;
2364fccf43aSdan         if( eType==SQLITE_INTEGER ){
2374fccf43aSdan           i = (u64)sqlite3_value_int64(pValue);
2384fccf43aSdan         }else{
2394fccf43aSdan           double r;
2404fccf43aSdan           assert( sizeof(double)==8 && sizeof(u64)==8 );
2414fccf43aSdan           r = sqlite3_value_double(pValue);
2424fccf43aSdan           memcpy(&i, &r, 8);
2434fccf43aSdan         }
244296c7658Sdan         sessionPutI64(&aBuf[1], i);
2454fccf43aSdan       }
2464fccf43aSdan       nByte = 9;
2474fccf43aSdan       break;
2484fccf43aSdan 
2494e895da1Sdan     default: {
2504fccf43aSdan       int n = sqlite3_value_bytes(pValue);
251296c7658Sdan       int nVarint = sessionVarintLen(n);
2524e895da1Sdan       assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
2534fccf43aSdan       if( aBuf ){
2544fccf43aSdan         sessionVarintPut(&aBuf[1], n);
2554fccf43aSdan         memcpy(&aBuf[nVarint + 1], eType==SQLITE_TEXT ?
2564fccf43aSdan             sqlite3_value_text(pValue) : sqlite3_value_blob(pValue), n
2574fccf43aSdan         );
2584fccf43aSdan       }
2594fccf43aSdan 
2604fccf43aSdan       nByte = 1 + nVarint + n;
2614fccf43aSdan       break;
2624fccf43aSdan     }
2634fccf43aSdan   }
2644fccf43aSdan 
2654fccf43aSdan   *pnWrite += nByte;
2664fccf43aSdan   return SQLITE_OK;
2674fccf43aSdan }
2684fccf43aSdan 
2694131639cSdan #define HASH_APPEND(hash, add) ((hash) << 3) ^ (hash) ^ (unsigned int)(add)
2704131639cSdan static unsigned int sessionHashAppendI64(unsigned int h, i64 i){
271e8d5648eSdan   h = HASH_APPEND(h, i & 0xFFFFFFFF);
272e8d5648eSdan   return HASH_APPEND(h, (i>>32)&0xFFFFFFFF);
273e8d5648eSdan }
2744131639cSdan static unsigned int sessionHashAppendBlob(unsigned int h, int n, const u8 *z){
275e8d5648eSdan   int i;
276e8d5648eSdan   for(i=0; i<n; i++) h = HASH_APPEND(h, z[i]);
277e8d5648eSdan   return h;
278e8d5648eSdan }
279e8d5648eSdan 
2804fccf43aSdan /*
2814131639cSdan ** This function may only be called from within a pre-update callback.
2824131639cSdan ** It calculates a hash based on the primary key values of the old.* or
2834131639cSdan ** new.* row currently available. The value returned is guaranteed to
2844131639cSdan ** be less than pTab->nBucket.
2854fccf43aSdan */
2864131639cSdan static unsigned int sessionPreupdateHash(
287e8d5648eSdan   sqlite3 *db,                    /* Database handle */
288e8d5648eSdan   SessionTable *pTab,             /* Session table handle */
289e8d5648eSdan   int bNew,                       /* True to hash the new.* PK */
29027453faeSdan   int *piHash,                    /* OUT: Hash value */
29127453faeSdan   int *pbNullPK
292e8d5648eSdan ){
2934131639cSdan   unsigned int h = 0;             /* Hash value to return */
2944131639cSdan   int i;                          /* Used to iterate through columns */
295e8d5648eSdan 
29627453faeSdan   assert( *pbNullPK==0 );
297e8d5648eSdan   assert( pTab->nCol==sqlite3_preupdate_count(db) );
298e8d5648eSdan   for(i=0; i<pTab->nCol; i++){
299e8d5648eSdan     if( pTab->abPK[i] ){
300e8d5648eSdan       int rc;
301e8d5648eSdan       int eType;
302e8d5648eSdan       sqlite3_value *pVal;
303e8d5648eSdan 
304e8d5648eSdan       if( bNew ){
305e8d5648eSdan         rc = sqlite3_preupdate_new(db, i, &pVal);
306e8d5648eSdan       }else{
307e8d5648eSdan         rc = sqlite3_preupdate_old(db, i, &pVal);
308e8d5648eSdan       }
30912ca0b56Sdan       if( rc!=SQLITE_OK ) return rc;
310e8d5648eSdan 
311e8d5648eSdan       eType = sqlite3_value_type(pVal);
312e8d5648eSdan       h = HASH_APPEND(h, eType);
313e8d5648eSdan       switch( eType ){
314e8d5648eSdan         case SQLITE_INTEGER:
315e8d5648eSdan         case SQLITE_FLOAT: {
316e8d5648eSdan           i64 iVal;
317e8d5648eSdan           if( eType==SQLITE_INTEGER ){
318e8d5648eSdan             iVal = sqlite3_value_int64(pVal);
319e8d5648eSdan           }else{
320e8d5648eSdan             double rVal = sqlite3_value_double(pVal);
321e8d5648eSdan             assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
322e8d5648eSdan             memcpy(&iVal, &rVal, 8);
323e8d5648eSdan           }
324e8d5648eSdan           h = sessionHashAppendI64(h, iVal);
325e8d5648eSdan           break;
326e8d5648eSdan         }
327e8d5648eSdan 
328e8d5648eSdan         case SQLITE_TEXT:
329e8d5648eSdan         case SQLITE_BLOB: {
330e8d5648eSdan           int n = sqlite3_value_bytes(pVal);
331e8d5648eSdan           const u8 *z = eType==SQLITE_TEXT ?
332e8d5648eSdan             sqlite3_value_text(pVal) : sqlite3_value_blob(pVal);
333e8d5648eSdan           h = sessionHashAppendBlob(h, n, z);
334e8d5648eSdan           break;
335e8d5648eSdan         }
33627453faeSdan 
33727453faeSdan         default:
33827453faeSdan           assert( eType==SQLITE_NULL );
33927453faeSdan           *pbNullPK = 1;
34027453faeSdan           return SQLITE_OK;
341e8d5648eSdan       }
342e8d5648eSdan     }
343e8d5648eSdan   }
344e8d5648eSdan 
345e8d5648eSdan   *piHash = (h % pTab->nChange);
346e8d5648eSdan   return SQLITE_OK;
347e8d5648eSdan }
348e8d5648eSdan 
3494131639cSdan /*
3504131639cSdan ** Based on the primary key values stored in change pChange, calculate a
3514131639cSdan ** hash key, assuming the has table has nBucket buckets. The hash keys
3524131639cSdan ** calculated by this function are compatible with those calculated by
3534131639cSdan ** sessionPreupdateHash().
3544131639cSdan */
3554131639cSdan static unsigned int sessionChangeHash(
3564131639cSdan   sqlite3 *db,                    /* Database handle */
3574131639cSdan   SessionTable *pTab,             /* Table handle */
3584131639cSdan   SessionChange *pChange,         /* Change handle */
3594131639cSdan   int nBucket                     /* Assume this many buckets in hash table */
360e8d5648eSdan ){
3614131639cSdan   unsigned int h = 0;             /* Value to return */
3624131639cSdan   int i;                          /* Used to iterate through columns */
3634131639cSdan   u8 *a = pChange->aRecord;       /* Used to iterate through change record */
364e8d5648eSdan 
365e8d5648eSdan   for(i=0; i<pTab->nCol; i++){
366e8d5648eSdan     int eType = *a++;
367e8d5648eSdan     int isPK = pTab->abPK[i];
368e8d5648eSdan 
36927453faeSdan     /* It is not possible for eType to be SQLITE_NULL here. The session
37027453faeSdan     ** module does not record changes for rows with NULL values stored in
37127453faeSdan     ** primary key columns. */
37227453faeSdan     assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
37327453faeSdan          || eType==SQLITE_TEXT || eType==SQLITE_BLOB
37427453faeSdan     );
37527453faeSdan 
376e8d5648eSdan     if( isPK ) h = HASH_APPEND(h, eType);
37727453faeSdan     if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
37827453faeSdan       if( isPK ) h = sessionHashAppendI64(h, sessionGetI64(a));
379e8d5648eSdan       a += 8;
38027453faeSdan     }else{
381e8d5648eSdan       int n;
382e8d5648eSdan       a += sessionVarintGet(a, &n);
38327453faeSdan       if( isPK ) h = sessionHashAppendBlob(h, n, a);
384e8d5648eSdan       a += n;
385e8d5648eSdan     }
386e8d5648eSdan   }
387e8d5648eSdan   return (h % nBucket);
388e8d5648eSdan }
389e8d5648eSdan 
390e8d5648eSdan static int sessionPreupdateEqual(
391e8d5648eSdan   sqlite3 *db,
392e8d5648eSdan   SessionTable *pTab,
393e8d5648eSdan   SessionChange *pChange,
394e8d5648eSdan   int bNew,
395e8d5648eSdan   int *pbEqual
396e8d5648eSdan ){
397e8d5648eSdan   int i;
398e8d5648eSdan   u8 *a = pChange->aRecord;
399e8d5648eSdan 
400e8d5648eSdan   *pbEqual = 0;
401e8d5648eSdan 
402e8d5648eSdan   for(i=0; i<pTab->nCol; i++){
403e8d5648eSdan     int eType = *a++;
404e8d5648eSdan     if( !pTab->abPK[i] ){
405e8d5648eSdan       switch( eType ){
406e8d5648eSdan         case SQLITE_INTEGER:
407e8d5648eSdan         case SQLITE_FLOAT:
408e8d5648eSdan           a += 8;
409e8d5648eSdan           break;
410e8d5648eSdan 
411e8d5648eSdan         case SQLITE_TEXT:
412e8d5648eSdan         case SQLITE_BLOB: {
413e8d5648eSdan           int n;
414e8d5648eSdan           a += sessionVarintGet(a, &n);
415e8d5648eSdan           a += n;
416e8d5648eSdan           break;
417e8d5648eSdan         }
418e8d5648eSdan       }
419e8d5648eSdan     }else{
420e8d5648eSdan       sqlite3_value *pVal;
421e8d5648eSdan       int rc;
422e8d5648eSdan       if( bNew ){
423e8d5648eSdan         rc = sqlite3_preupdate_new(db, i, &pVal);
424e8d5648eSdan       }else{
425e8d5648eSdan         rc = sqlite3_preupdate_old(db, i, &pVal);
426e8d5648eSdan       }
427e8d5648eSdan       if( rc!=SQLITE_OK || sqlite3_value_type(pVal)!=eType ) return rc;
428e8d5648eSdan 
42912ca0b56Sdan       /* A SessionChange object never has a NULL value in a PK column */
43012ca0b56Sdan       assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
43112ca0b56Sdan            || eType==SQLITE_BLOB    || eType==SQLITE_TEXT
43212ca0b56Sdan       );
43312ca0b56Sdan 
43412ca0b56Sdan       if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
435e8d5648eSdan         i64 iVal = sessionGetI64(a);
436e8d5648eSdan         a += 8;
437e8d5648eSdan         if( eType==SQLITE_INTEGER ){
438e8d5648eSdan           if( sqlite3_value_int64(pVal)!=iVal ) return SQLITE_OK;
439e8d5648eSdan         }else{
440e8d5648eSdan           double rVal;
441e8d5648eSdan           assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
442e8d5648eSdan           memcpy(&rVal, &iVal, 8);
443e8d5648eSdan           if( sqlite3_value_double(pVal)!=rVal ) return SQLITE_OK;
444e8d5648eSdan         }
44512ca0b56Sdan       }else{
446e8d5648eSdan         int n;
447e8d5648eSdan         const u8 *z;
448e8d5648eSdan         a += sessionVarintGet(a, &n);
449e8d5648eSdan         if( sqlite3_value_bytes(pVal)!=n ) return SQLITE_OK;
45012ca0b56Sdan         if( eType==SQLITE_TEXT ){
45112ca0b56Sdan           z = sqlite3_value_text(pVal);
45212ca0b56Sdan         }else{
45312ca0b56Sdan           z = sqlite3_value_blob(pVal);
45412ca0b56Sdan         }
455e8d5648eSdan         if( memcmp(a, z, n) ) return SQLITE_OK;
456e8d5648eSdan         a += n;
457e8d5648eSdan         break;
458e8d5648eSdan       }
459e8d5648eSdan     }
460e8d5648eSdan   }
461e8d5648eSdan 
462e8d5648eSdan   *pbEqual = 1;
463e8d5648eSdan   return SQLITE_OK;
4644fccf43aSdan }
4654fccf43aSdan 
4664fccf43aSdan /*
4674fccf43aSdan ** If required, grow the hash table used to store changes on table pTab
4684fccf43aSdan ** (part of the session pSession). If a fatal OOM error occurs, set the
4694fccf43aSdan ** session object to failed and return SQLITE_ERROR. Otherwise, return
4704fccf43aSdan ** SQLITE_OK.
4714fccf43aSdan **
4724fccf43aSdan ** It is possible that a non-fatal OOM error occurs in this function. In
4734fccf43aSdan ** that case the hash-table does not grow, but SQLITE_OK is returned anyway.
4744fccf43aSdan ** Growing the hash table in this case is a performance optimization only,
4754fccf43aSdan ** it is not required for correct operation.
4764fccf43aSdan */
4774fccf43aSdan static int sessionGrowHash(sqlite3_session *pSession, SessionTable *pTab){
4784fccf43aSdan   if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
4794fccf43aSdan     int i;
4804fccf43aSdan     SessionChange **apNew;
4814fccf43aSdan     int nNew = (pTab->nChange ? pTab->nChange : 128) * 2;
4824fccf43aSdan 
4834fccf43aSdan     apNew = (SessionChange **)sqlite3_malloc(sizeof(SessionChange *) * nNew);
4844fccf43aSdan     if( apNew==0 ){
4854fccf43aSdan       if( pTab->nChange==0 ){
4864fccf43aSdan         pSession->rc = SQLITE_NOMEM;
4874fccf43aSdan         return SQLITE_ERROR;
4884fccf43aSdan       }
4894fccf43aSdan       return SQLITE_OK;
4904fccf43aSdan     }
4914fccf43aSdan     memset(apNew, 0, sizeof(SessionChange *) * nNew);
4924fccf43aSdan 
4934fccf43aSdan     for(i=0; i<pTab->nChange; i++){
4944fccf43aSdan       SessionChange *p;
4954fccf43aSdan       SessionChange *pNext;
4964fccf43aSdan       for(p=pTab->apChange[i]; p; p=pNext){
497e8d5648eSdan         int iHash = sessionChangeHash(pSession->db, pTab, p, nNew);
4984fccf43aSdan         pNext = p->pNext;
4994fccf43aSdan         p->pNext = apNew[iHash];
5004fccf43aSdan         apNew[iHash] = p;
5014fccf43aSdan       }
5024fccf43aSdan     }
5034fccf43aSdan 
5044fccf43aSdan     sqlite3_free(pTab->apChange);
5054fccf43aSdan     pTab->nChange = nNew;
5064fccf43aSdan     pTab->apChange = apNew;
5074fccf43aSdan   }
5084fccf43aSdan 
5094fccf43aSdan   return SQLITE_OK;
5104fccf43aSdan }
5114fccf43aSdan 
512296c7658Sdan /*
513e8d5648eSdan ** This function queries the database for the names of the columns of table
514e8d5648eSdan ** zThis, in schema zDb. It is expected that the table has nCol columns. If
515e8d5648eSdan ** not, SQLITE_SCHEMA is returned and none of the output variables are
516e8d5648eSdan ** populated.
517e8d5648eSdan **
518e8d5648eSdan ** Otherwise, if it is not NULL, variable *pzTab is set to point to a
519e8d5648eSdan ** nul-terminated copy of the table name. *pazCol (if not NULL) is set to
520e8d5648eSdan ** point to an array of pointers to column names. And *pabPK (again, if not
521e8d5648eSdan ** NULL) is set to point to an array of booleans - true if the corresponding
522e8d5648eSdan ** column is part of the primary key.
523e8d5648eSdan **
524e8d5648eSdan ** For example, if the table is declared as:
525e8d5648eSdan **
526e8d5648eSdan **     CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z));
527e8d5648eSdan **
528e8d5648eSdan ** Then the three output variables are populated as follows:
529e8d5648eSdan **
530e8d5648eSdan **     *pzTab  = "tbl1"
531e8d5648eSdan **     *pazCol = {"w", "x", "y", "z"}
532e8d5648eSdan **     *pabPK  = {1, 0, 0, 1}
533e8d5648eSdan **
534e8d5648eSdan ** All returned buffers are part of the same single allocation, which must
535e8d5648eSdan ** be freed using sqlite3_free() by the caller. If pazCol was not NULL, then
536e8d5648eSdan ** pointer *pazCol should be freed to release all memory. Otherwise, pointer
537e8d5648eSdan ** *pabPK. It is illegal for both pazCol and pabPK to be NULL.
538e8d5648eSdan */
539e8d5648eSdan static int sessionTableInfo(
540e8d5648eSdan   sqlite3 *db,                    /* Database connection */
541e8d5648eSdan   const char *zDb,                /* Name of attached database (e.g. "main") */
542e8d5648eSdan   const char *zThis,              /* Table name */
543e8d5648eSdan   int nCol,                       /* Expected number of columns */
544e8d5648eSdan   const char **pzTab,             /* OUT: Copy of zThis */
545e8d5648eSdan   const char ***pazCol,           /* OUT: Array of column names for table */
546e8d5648eSdan   u8 **pabPK                      /* OUT: Array of booleans - true for PK col */
547e8d5648eSdan ){
548e8d5648eSdan   char *zPragma;
549e8d5648eSdan   sqlite3_stmt *pStmt;
550e8d5648eSdan   int rc;
551e8d5648eSdan   int nByte;
552e8d5648eSdan   int nDbCol = 0;
553e8d5648eSdan   int nThis;
554e8d5648eSdan   int i;
555e8d5648eSdan   u8 *pAlloc;
556db04571cSdan   char **azCol = 0;
557e8d5648eSdan   u8 *abPK;
558e8d5648eSdan 
559db04571cSdan   assert( pazCol && pabPK );
560e8d5648eSdan 
561e8d5648eSdan   nThis = strlen(zThis);
562e8d5648eSdan   zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
563e8d5648eSdan   if( !zPragma ) return SQLITE_NOMEM;
564e8d5648eSdan 
565e8d5648eSdan   rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
566e8d5648eSdan   sqlite3_free(zPragma);
567e8d5648eSdan   if( rc!=SQLITE_OK ) return rc;
568e8d5648eSdan 
569e8d5648eSdan   nByte = nThis + 1;
570e8d5648eSdan   while( SQLITE_ROW==sqlite3_step(pStmt) ){
571e8d5648eSdan     nByte += sqlite3_column_bytes(pStmt, 1);
572e8d5648eSdan     nDbCol++;
573e8d5648eSdan   }
574e8d5648eSdan   rc = sqlite3_reset(pStmt);
575e8d5648eSdan 
576e8d5648eSdan   if( nDbCol!=nCol ){
577e8d5648eSdan     rc = SQLITE_SCHEMA;
578e8d5648eSdan   }
579e8d5648eSdan   if( rc==SQLITE_OK ){
580e8d5648eSdan     nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
581e8d5648eSdan     pAlloc = sqlite3_malloc(nByte);
582e8d5648eSdan     if( pAlloc==0 ){
583e8d5648eSdan       rc = SQLITE_NOMEM;
584e8d5648eSdan     }
585e8d5648eSdan   }
586e8d5648eSdan   if( rc==SQLITE_OK ){
587e8d5648eSdan     azCol = (char **)pAlloc;
588e8d5648eSdan     pAlloc = (u8 *)&azCol[nCol];
589e8d5648eSdan     abPK = (u8 *)pAlloc;
590e8d5648eSdan     pAlloc = &abPK[nCol];
591e8d5648eSdan     if( pzTab ){
592e8d5648eSdan       memcpy(pAlloc, zThis, nThis+1);
593e8d5648eSdan       *pzTab = (char *)pAlloc;
594e8d5648eSdan       pAlloc += nThis+1;
595e8d5648eSdan     }
596e8d5648eSdan 
597e8d5648eSdan     i = 0;
598e8d5648eSdan     while( SQLITE_ROW==sqlite3_step(pStmt) ){
599e8d5648eSdan       int nName = sqlite3_column_bytes(pStmt, 1);
600e8d5648eSdan       const unsigned char *zName = sqlite3_column_text(pStmt, 1);
601e8d5648eSdan       if( zName==0 ) break;
602e8d5648eSdan       memcpy(pAlloc, zName, nName+1);
603e8d5648eSdan       azCol[i] = (char *)pAlloc;
604e8d5648eSdan       pAlloc += nName+1;
605db04571cSdan       abPK[i] = sqlite3_column_int(pStmt, 5);
606e8d5648eSdan       i++;
607e8d5648eSdan     }
608e8d5648eSdan     rc = sqlite3_reset(pStmt);
609e8d5648eSdan 
610e8d5648eSdan   }
611e8d5648eSdan 
612e8d5648eSdan   /* If successful, populate the output variables. Otherwise, zero them and
613e8d5648eSdan   ** free any allocation made. An error code will be returned in this case.
614e8d5648eSdan   */
615e8d5648eSdan   if( rc==SQLITE_OK ){
616db04571cSdan     *pazCol = (const char **)azCol;
617db04571cSdan     *pabPK = abPK;
618e8d5648eSdan   }else{
619db04571cSdan     *pazCol = 0;
620db04571cSdan     *pabPK = 0;
621e8d5648eSdan     if( pzTab ) *pzTab = 0;
622db04571cSdan     sqlite3_free(azCol);
623e8d5648eSdan   }
624e8d5648eSdan   sqlite3_finalize(pStmt);
625e8d5648eSdan   return rc;
626e8d5648eSdan }
627e8d5648eSdan 
628e8d5648eSdan /*
629296c7658Sdan ** This function is only called from within a pre-update handler for a
630296c7658Sdan ** write to table pTab, part of session pSession. If this is the first
631296c7658Sdan ** write to this table, set the SessionTable.nCol variable to the number
632296c7658Sdan ** of columns in the table.
633296c7658Sdan **
634296c7658Sdan ** Otherwise, if this is not the first time this table has been written
635296c7658Sdan ** to, check that the number of columns in the table has not changed. If
636296c7658Sdan ** it has not, return zero.
637296c7658Sdan **
638296c7658Sdan ** If the number of columns in the table has changed since the last write
639296c7658Sdan ** was recorded, set the session error-code to SQLITE_SCHEMA and return
640296c7658Sdan ** non-zero. Users are not allowed to change the number of columns in a table
641296c7658Sdan ** for which changes are being recorded by the session module. If they do so,
642296c7658Sdan ** it is an error.
643296c7658Sdan */
6444fccf43aSdan static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
6454fccf43aSdan   if( pTab->nCol==0 ){
646e8d5648eSdan     assert( pTab->azCol==0 || pTab->abPK==0 );
6474fccf43aSdan     pTab->nCol = sqlite3_preupdate_count(pSession->db);
648e8d5648eSdan     pSession->rc = sessionTableInfo(pSession->db, pSession->zDb,
649e8d5648eSdan         pTab->zName, pTab->nCol, 0, &pTab->azCol, &pTab->abPK
650e8d5648eSdan     );
651296c7658Sdan   }else if( pTab->nCol!=sqlite3_preupdate_count(pSession->db) ){
6524fccf43aSdan     pSession->rc = SQLITE_SCHEMA;
6534fccf43aSdan   }
654e8d5648eSdan   return pSession->rc;
655e8d5648eSdan }
656e8d5648eSdan 
657e8d5648eSdan static void sessionPreupdateOneChange(
658e8d5648eSdan   int op,
659e8d5648eSdan   sqlite3_session *pSession,
660e8d5648eSdan   SessionTable *pTab
661e8d5648eSdan ){
662e8d5648eSdan   sqlite3 *db = pSession->db;
663e8d5648eSdan   SessionChange *pChange;
664e8d5648eSdan   SessionChange *pC;
665e8d5648eSdan   int iHash;
66627453faeSdan   int bNullPk = 0;
667e8d5648eSdan   int rc = SQLITE_OK;
668e8d5648eSdan 
669e8d5648eSdan   if( pSession->rc ) return;
670e8d5648eSdan 
671e8d5648eSdan   /* Load table details if required */
672e8d5648eSdan   if( sessionInitTable(pSession, pTab) ) return;
673e8d5648eSdan 
674e8d5648eSdan   /* Grow the hash table if required */
675e8d5648eSdan   if( sessionGrowHash(pSession, pTab) ) return;
676e8d5648eSdan 
677e8d5648eSdan   /* Search the hash table for an existing entry for rowid=iKey2. If
678e8d5648eSdan   ** one is found, store a pointer to it in pChange and unlink it from
679e8d5648eSdan   ** the hash table. Otherwise, set pChange to NULL.
680e8d5648eSdan   */
68127453faeSdan   rc = sessionPreupdateHash(db, pTab, op==SQLITE_INSERT, &iHash, &bNullPk);
68227453faeSdan   if( bNullPk==0 ){
683e8d5648eSdan     for(pC=pTab->apChange[iHash]; rc==SQLITE_OK && pC; pC=pC->pNext){
684e8d5648eSdan       int bEqual;
685e8d5648eSdan       rc = sessionPreupdateEqual(db, pTab, pC, op==SQLITE_INSERT, &bEqual);
686e8d5648eSdan       if( bEqual ) break;
687e8d5648eSdan     }
688e8d5648eSdan     if( pC==0 ){
689e8d5648eSdan       /* Create a new change object containing all the old values (if
690e8d5648eSdan        ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK
691e8d5648eSdan        ** values (if this is an INSERT). */
692e8d5648eSdan       int nByte;              /* Number of bytes to allocate */
693e8d5648eSdan       int i;                  /* Used to iterate through columns */
694e8d5648eSdan 
695e8d5648eSdan       pTab->nEntry++;
696e8d5648eSdan 
697e8d5648eSdan       /* Figure out how large an allocation is required */
698e8d5648eSdan       nByte = sizeof(SessionChange);
699e8d5648eSdan       for(i=0; i<pTab->nCol && rc==SQLITE_OK; i++){
700e8d5648eSdan         sqlite3_value *p = 0;
701e8d5648eSdan         if( op!=SQLITE_INSERT ){
702e8d5648eSdan           rc = sqlite3_preupdate_old(pSession->db, i, &p);
703e8d5648eSdan         }else if( 1 || pTab->abPK[i] ){
704e8d5648eSdan           rc = sqlite3_preupdate_new(pSession->db, i, &p);
705e8d5648eSdan         }
706e8d5648eSdan         if( p && rc==SQLITE_OK ){
707e8d5648eSdan           rc = sessionSerializeValue(0, p, &nByte);
708e8d5648eSdan         }
709e8d5648eSdan       }
710e8d5648eSdan 
711e8d5648eSdan       /* Allocate the change object */
712e8d5648eSdan       pChange = (SessionChange *)sqlite3_malloc(nByte);
713e8d5648eSdan       if( !pChange ){
714e8d5648eSdan         rc = SQLITE_NOMEM;
715e8d5648eSdan       }else{
716e8d5648eSdan         memset(pChange, 0, sizeof(SessionChange));
717e8d5648eSdan         pChange->aRecord = (u8 *)&pChange[1];
718e8d5648eSdan       }
719e8d5648eSdan 
720e8d5648eSdan       /* Populate the change object */
721e8d5648eSdan       nByte = 0;
722e8d5648eSdan       for(i=0; i<pTab->nCol && rc==SQLITE_OK; i++){
723e8d5648eSdan         sqlite3_value *p = 0;
724e8d5648eSdan         if( op!=SQLITE_INSERT ){
725e8d5648eSdan           rc = sqlite3_preupdate_old(pSession->db, i, &p);
726e8d5648eSdan         }else if( 1 || pTab->abPK[i] ){
727e8d5648eSdan           rc = sqlite3_preupdate_new(pSession->db, i, &p);
728e8d5648eSdan         }
729e8d5648eSdan         if( p && rc==SQLITE_OK ){
730e8d5648eSdan           rc = sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
731e8d5648eSdan         }
732e8d5648eSdan       }
73312ca0b56Sdan       if( rc==SQLITE_OK ){
734e8d5648eSdan         /* Add the change back to the hash-table */
73512ca0b56Sdan         pChange->nRecord = nByte;
736e8d5648eSdan         pChange->bInsert = (op==SQLITE_INSERT);
737e8d5648eSdan         pChange->pNext = pTab->apChange[iHash];
738e8d5648eSdan         pTab->apChange[iHash] = pChange;
73912ca0b56Sdan       }else{
74012ca0b56Sdan         sqlite3_free(pChange);
741e8d5648eSdan       }
742e8d5648eSdan     }
7434fccf43aSdan   }
74412ca0b56Sdan 
74512ca0b56Sdan   /* If an error has occurred, mark the session object as failed. */
74612ca0b56Sdan   if( rc!=SQLITE_OK ){
74712ca0b56Sdan     pSession->rc = rc;
74812ca0b56Sdan   }
74927453faeSdan }
7504fccf43aSdan 
7514fccf43aSdan /*
7524fccf43aSdan ** The 'pre-update' hook registered by this module with SQLite databases.
7534fccf43aSdan */
7544fccf43aSdan static void xPreUpdate(
7554fccf43aSdan   void *pCtx,                     /* Copy of third arg to preupdate_hook() */
7564fccf43aSdan   sqlite3 *db,                    /* Database handle */
7574fccf43aSdan   int op,                         /* SQLITE_UPDATE, DELETE or INSERT */
7584fccf43aSdan   char const *zDb,                /* Database name */
7594fccf43aSdan   char const *zName,              /* Table name */
7604fccf43aSdan   sqlite3_int64 iKey1,            /* Rowid of row about to be deleted/updated */
7614fccf43aSdan   sqlite3_int64 iKey2             /* New rowid value (for a rowid UPDATE) */
7624fccf43aSdan ){
7634fccf43aSdan   sqlite3_session *pSession;
7644fccf43aSdan   int nDb = strlen(zDb);
7654fccf43aSdan   int nName = strlen(zDb);
7664fccf43aSdan 
7674c220252Sdan   assert( sqlite3_mutex_held(db->mutex) );
7684c220252Sdan 
7694fccf43aSdan   for(pSession=(sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){
7704fccf43aSdan     SessionTable *pTab;
771296c7658Sdan 
772e8d5648eSdan     /* If this session is attached to a different database ("main", "temp"
773e8d5648eSdan     ** etc.), or if it is not currently enabled, there is nothing to do. Skip
774e8d5648eSdan     ** to the next session object attached to this database. */
775296c7658Sdan     if( pSession->bEnable==0 ) continue;
7764fccf43aSdan     if( pSession->rc ) continue;
7774fccf43aSdan     if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue;
778296c7658Sdan 
779*ff4d0f41Sdan     for(pTab=pSession->pTable; pTab || pSession->bAutoAttach; pTab=pTab->pNext){
780*ff4d0f41Sdan       if( !pTab ){
781*ff4d0f41Sdan         /* This branch is taken if table zName has not yet been attached to
782*ff4d0f41Sdan         ** this session and the auto-attach flag is set.  */
783*ff4d0f41Sdan         pSession->rc = sqlite3session_attach(pSession,zName);
784*ff4d0f41Sdan         if( pSession->rc ) continue;
785*ff4d0f41Sdan         pTab = pSession->pTable;
786*ff4d0f41Sdan         assert( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) );
787*ff4d0f41Sdan       }
788*ff4d0f41Sdan 
7894fccf43aSdan       if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ){
790e8d5648eSdan         sessionPreupdateOneChange(op, pSession, pTab);
791e8d5648eSdan         if( op==SQLITE_UPDATE ){
792e8d5648eSdan           sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab);
7934fccf43aSdan         }
7944fccf43aSdan         break;
7954fccf43aSdan       }
7964fccf43aSdan     }
7974fccf43aSdan   }
798296c7658Sdan }
7994fccf43aSdan 
8004fccf43aSdan /*
8014fccf43aSdan ** Create a session object. This session object will record changes to
8024fccf43aSdan ** database zDb attached to connection db.
8034fccf43aSdan */
8044fccf43aSdan int sqlite3session_create(
8054fccf43aSdan   sqlite3 *db,                    /* Database handle */
8064fccf43aSdan   const char *zDb,                /* Name of db (e.g. "main") */
8074fccf43aSdan   sqlite3_session **ppSession     /* OUT: New session object */
8084fccf43aSdan ){
809296c7658Sdan   sqlite3_session *pNew;          /* Newly allocated session object */
810296c7658Sdan   sqlite3_session *pOld;          /* Session object already attached to db */
8114fccf43aSdan   int nDb = strlen(zDb);          /* Length of zDb in bytes */
8124fccf43aSdan 
813296c7658Sdan   /* Zero the output value in case an error occurs. */
8144fccf43aSdan   *ppSession = 0;
8154fccf43aSdan 
8164fccf43aSdan   /* Allocate and populate the new session object. */
8174fccf43aSdan   pNew = (sqlite3_session *)sqlite3_malloc(sizeof(sqlite3_session) + nDb + 1);
8184fccf43aSdan   if( !pNew ) return SQLITE_NOMEM;
8194fccf43aSdan   memset(pNew, 0, sizeof(sqlite3_session));
8204fccf43aSdan   pNew->db = db;
8214fccf43aSdan   pNew->zDb = (char *)&pNew[1];
822296c7658Sdan   pNew->bEnable = 1;
8234fccf43aSdan   memcpy(pNew->zDb, zDb, nDb+1);
8244fccf43aSdan 
8254fccf43aSdan   /* Add the new session object to the linked list of session objects
8264fccf43aSdan   ** attached to database handle $db. Do this under the cover of the db
8274fccf43aSdan   ** handle mutex.  */
8284fccf43aSdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
8294fccf43aSdan   pOld = (sqlite3_session*)sqlite3_preupdate_hook(db, xPreUpdate, (void*)pNew);
8304fccf43aSdan   pNew->pNext = pOld;
8314fccf43aSdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
8324fccf43aSdan 
8334fccf43aSdan   *ppSession = pNew;
8344fccf43aSdan   return SQLITE_OK;
8354fccf43aSdan }
8364fccf43aSdan 
8374fccf43aSdan /*
8384fccf43aSdan ** Delete a session object previously allocated using sqlite3session_create().
8394fccf43aSdan */
8404fccf43aSdan void sqlite3session_delete(sqlite3_session *pSession){
8414fccf43aSdan   sqlite3 *db = pSession->db;
8424fccf43aSdan   sqlite3_session *pHead;
8434fccf43aSdan   sqlite3_session **pp;
8444fccf43aSdan 
845296c7658Sdan   /* Unlink the session from the linked list of sessions attached to the
846296c7658Sdan   ** database handle. Hold the db mutex while doing so.  */
8474fccf43aSdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
8484fccf43aSdan   pHead = (sqlite3_session*)sqlite3_preupdate_hook(db, 0, 0);
8494fccf43aSdan   for(pp=&pHead; (*pp)!=pSession; pp=&((*pp)->pNext));
8504fccf43aSdan   *pp = (*pp)->pNext;
8514fccf43aSdan   if( pHead ) sqlite3_preupdate_hook(db, xPreUpdate, (void *)pHead);
8524fccf43aSdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
8534fccf43aSdan 
854296c7658Sdan   /* Delete all attached table objects. And the contents of their
855296c7658Sdan   ** associated hash-tables. */
8564fccf43aSdan   while( pSession->pTable ){
8574fccf43aSdan     int i;
8584fccf43aSdan     SessionTable *pTab = pSession->pTable;
8594fccf43aSdan     pSession->pTable = pTab->pNext;
8604fccf43aSdan     for(i=0; i<pTab->nChange; i++){
8614fccf43aSdan       SessionChange *p;
8624fccf43aSdan       SessionChange *pNext;
8634fccf43aSdan       for(p=pTab->apChange[i]; p; p=pNext){
8644fccf43aSdan         pNext = p->pNext;
8654fccf43aSdan         sqlite3_free(p);
8664fccf43aSdan       }
8674fccf43aSdan     }
868e8d5648eSdan     sqlite3_free(pTab->azCol);
8694fccf43aSdan     sqlite3_free(pTab->apChange);
8704fccf43aSdan     sqlite3_free(pTab);
8714fccf43aSdan   }
8724fccf43aSdan 
873296c7658Sdan   /* Free the session object itself. */
8744fccf43aSdan   sqlite3_free(pSession);
8754fccf43aSdan }
8764fccf43aSdan 
8774fccf43aSdan /*
8784fccf43aSdan ** Attach a table to a session. All subsequent changes made to the table
8794fccf43aSdan ** while the session object is enabled will be recorded.
8804fccf43aSdan **
8814fccf43aSdan ** Only tables that have a PRIMARY KEY defined may be attached. It does
8824fccf43aSdan ** not matter if the PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias)
8834fccf43aSdan ** or not.
8844fccf43aSdan */
8854fccf43aSdan int sqlite3session_attach(
8864fccf43aSdan   sqlite3_session *pSession,      /* Session object */
8874fccf43aSdan   const char *zName               /* Table name */
8884fccf43aSdan ){
889*ff4d0f41Sdan   int rc = SQLITE_OK;
890*ff4d0f41Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
891*ff4d0f41Sdan 
892*ff4d0f41Sdan   if( !zName ){
893*ff4d0f41Sdan     pSession->bAutoAttach = 1;
894*ff4d0f41Sdan   }else{
895296c7658Sdan     SessionTable *pTab;           /* New table object (if required) */
896296c7658Sdan     int nName;                    /* Number of bytes in string zName */
8974fccf43aSdan 
8984fccf43aSdan     /* First search for an existing entry. If one is found, this call is
8994fccf43aSdan     ** a no-op. Return early. */
9004fccf43aSdan     nName = strlen(zName);
9014fccf43aSdan     for(pTab=pSession->pTable; pTab; pTab=pTab->pNext){
9024c220252Sdan       if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ) break;
9034fccf43aSdan     }
9044fccf43aSdan 
9054c220252Sdan     if( !pTab ){
9064fccf43aSdan       /* Allocate new SessionTable object. */
9074fccf43aSdan       pTab = (SessionTable *)sqlite3_malloc(sizeof(SessionTable) + nName + 1);
9084c220252Sdan       if( !pTab ){
9094c220252Sdan         rc = SQLITE_NOMEM;
9104c220252Sdan       }else{
9114fccf43aSdan         /* Populate the new SessionTable object and link it into the list. */
9124fccf43aSdan         memset(pTab, 0, sizeof(SessionTable));
9134fccf43aSdan         pTab->zName = (char *)&pTab[1];
9144fccf43aSdan         memcpy(pTab->zName, zName, nName+1);
9154fccf43aSdan         pTab->pNext = pSession->pTable;
9164fccf43aSdan         pSession->pTable = pTab;
9174c220252Sdan       }
9184c220252Sdan     }
919*ff4d0f41Sdan   }
9204fccf43aSdan 
9214c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
9224c220252Sdan   return rc;
9234fccf43aSdan }
9244fccf43aSdan 
925296c7658Sdan /*
926296c7658Sdan ** Ensure that there is room in the buffer to append nByte bytes of data.
927296c7658Sdan ** If not, use sqlite3_realloc() to grow the buffer so that there is.
928296c7658Sdan **
929296c7658Sdan ** If successful, return zero. Otherwise, if an OOM condition is encountered,
930296c7658Sdan ** set *pRc to SQLITE_NOMEM and return non-zero.
931296c7658Sdan */
9324fccf43aSdan static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){
9334fccf43aSdan   if( p->nAlloc-p->nBuf<nByte ){
9344fccf43aSdan     u8 *aNew;
9354fccf43aSdan     int nNew = p->nAlloc ? p->nAlloc : 128;
9364fccf43aSdan     do {
9374fccf43aSdan       nNew = nNew*2;
9384fccf43aSdan     }while( nNew<(p->nAlloc+nByte) );
9394fccf43aSdan 
9404fccf43aSdan     aNew = (u8 *)sqlite3_realloc(p->aBuf, nNew);
9414fccf43aSdan     if( 0==aNew ){
9424fccf43aSdan       *pRc = SQLITE_NOMEM;
9434fccf43aSdan       return 1;
9444fccf43aSdan     }
9454fccf43aSdan     p->aBuf = aNew;
9464fccf43aSdan     p->nAlloc = nNew;
9474fccf43aSdan   }
9484fccf43aSdan   return 0;
9494fccf43aSdan }
9504fccf43aSdan 
951296c7658Sdan /*
952296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
953296c7658Sdan ** called. Otherwise, append a single byte to the buffer.
954296c7658Sdan **
955296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
956296c7658Sdan ** returning.
957296c7658Sdan */
9584fccf43aSdan static void sessionAppendByte(SessionBuffer *p, u8 v, int *pRc){
9594fccf43aSdan   if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, 1, pRc) ){
9604fccf43aSdan     p->aBuf[p->nBuf++] = v;
9614fccf43aSdan   }
9624fccf43aSdan }
9634fccf43aSdan 
964296c7658Sdan /*
965296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
966296c7658Sdan ** called. Otherwise, append a single varint to the buffer.
967296c7658Sdan **
968296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
969296c7658Sdan ** returning.
970296c7658Sdan */
9714fccf43aSdan static void sessionAppendVarint(SessionBuffer *p, sqlite3_int64 v, int *pRc){
9724fccf43aSdan   if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, 9, pRc) ){
9734fccf43aSdan     p->nBuf += sessionVarintPut(&p->aBuf[p->nBuf], v);
9744fccf43aSdan   }
9754fccf43aSdan }
9764fccf43aSdan 
977296c7658Sdan /*
978296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
979296c7658Sdan ** called. Otherwise, append a blob of data to the buffer.
980296c7658Sdan **
981296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
982296c7658Sdan ** returning.
983296c7658Sdan */
9844fccf43aSdan static void sessionAppendBlob(
9854fccf43aSdan   SessionBuffer *p,
9864fccf43aSdan   const u8 *aBlob,
9874fccf43aSdan   int nBlob,
9884fccf43aSdan   int *pRc
9894fccf43aSdan ){
9904fccf43aSdan   if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nBlob, pRc) ){
9914fccf43aSdan     memcpy(&p->aBuf[p->nBuf], aBlob, nBlob);
9924fccf43aSdan     p->nBuf += nBlob;
9934fccf43aSdan   }
9944fccf43aSdan }
9954fccf43aSdan 
996296c7658Sdan /*
997296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
998296c7658Sdan ** called. Otherwise, append a string to the buffer. All bytes in the string
999296c7658Sdan ** up to (but not including) the nul-terminator are written to the buffer.
1000296c7658Sdan **
1001296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1002296c7658Sdan ** returning.
1003296c7658Sdan */
1004d5f0767cSdan static void sessionAppendStr(
1005d5f0767cSdan   SessionBuffer *p,
1006d5f0767cSdan   const char *zStr,
1007d5f0767cSdan   int *pRc
1008d5f0767cSdan ){
1009d5f0767cSdan   int nStr = strlen(zStr);
1010d5f0767cSdan   if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nStr, pRc) ){
1011d5f0767cSdan     memcpy(&p->aBuf[p->nBuf], zStr, nStr);
1012d5f0767cSdan     p->nBuf += nStr;
1013d5f0767cSdan   }
1014d5f0767cSdan }
1015d5f0767cSdan 
1016296c7658Sdan /*
1017296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1018296c7658Sdan ** called. Otherwise, append the string representation of integer iVal
1019296c7658Sdan ** to the buffer. No nul-terminator is written.
1020296c7658Sdan **
1021296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1022296c7658Sdan ** returning.
1023296c7658Sdan */
1024d5f0767cSdan static void sessionAppendInteger(
1025296c7658Sdan   SessionBuffer *p,               /* Buffer to append to */
1026296c7658Sdan   int iVal,                       /* Value to write the string rep. of */
1027296c7658Sdan   int *pRc                        /* IN/OUT: Error code */
1028d5f0767cSdan ){
1029d5f0767cSdan   char aBuf[24];
1030d5f0767cSdan   sqlite3_snprintf(sizeof(aBuf)-1, aBuf, "%d", iVal);
1031d5f0767cSdan   sessionAppendStr(p, aBuf, pRc);
1032d5f0767cSdan }
1033d5f0767cSdan 
1034296c7658Sdan /*
1035296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1036296c7658Sdan ** called. Otherwise, append the string zStr enclosed in quotes (") and
1037296c7658Sdan ** with any embedded quote characters escaped to the buffer. No
1038296c7658Sdan ** nul-terminator byte is written.
1039296c7658Sdan **
1040296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1041296c7658Sdan ** returning.
1042296c7658Sdan */
1043d5f0767cSdan static void sessionAppendIdent(
1044296c7658Sdan   SessionBuffer *p,               /* Buffer to a append to */
1045296c7658Sdan   const char *zStr,               /* String to quote, escape and append */
1046296c7658Sdan   int *pRc                        /* IN/OUT: Error code */
1047d5f0767cSdan ){
1048d5f0767cSdan   int nStr = strlen(zStr)*2 + 2 + 1;
1049d5f0767cSdan   if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nStr, pRc) ){
1050d5f0767cSdan     char *zOut = (char *)&p->aBuf[p->nBuf];
1051d5f0767cSdan     const char *zIn = zStr;
1052d5f0767cSdan     *zOut++ = '"';
1053d5f0767cSdan     while( *zIn ){
1054d5f0767cSdan       if( *zIn=='"' ) *zOut++ = '"';
1055d5f0767cSdan       *zOut++ = *(zIn++);
1056d5f0767cSdan     }
1057d5f0767cSdan     *zOut++ = '"';
1058d5f0767cSdan     p->nBuf = ((u8 *)zOut - p->aBuf);
1059d5f0767cSdan   }
1060d5f0767cSdan }
1061d5f0767cSdan 
1062296c7658Sdan /*
1063296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1064296c7658Sdan ** called. Otherwse, it appends the serialized version of the value stored
1065296c7658Sdan ** in column iCol of the row that SQL statement pStmt currently points
1066296c7658Sdan ** to to the buffer.
1067296c7658Sdan */
10684fccf43aSdan static void sessionAppendCol(
1069296c7658Sdan   SessionBuffer *p,               /* Buffer to append to */
1070296c7658Sdan   sqlite3_stmt *pStmt,            /* Handle pointing to row containing value */
1071296c7658Sdan   int iCol,                       /* Column to read value from */
1072296c7658Sdan   int *pRc                        /* IN/OUT: Error code */
10734fccf43aSdan ){
10744fccf43aSdan   if( *pRc==SQLITE_OK ){
10754fccf43aSdan     int eType = sqlite3_column_type(pStmt, iCol);
10764fccf43aSdan     sessionAppendByte(p, (u8)eType, pRc);
10774fccf43aSdan     if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
10784fccf43aSdan       sqlite3_int64 i;
10794fccf43aSdan       u8 aBuf[8];
10804fccf43aSdan       if( eType==SQLITE_INTEGER ){
10814fccf43aSdan         i = sqlite3_column_int64(pStmt, iCol);
10824fccf43aSdan       }else{
10834fccf43aSdan         double r = sqlite3_column_double(pStmt, iCol);
10844fccf43aSdan         memcpy(&i, &r, 8);
10854fccf43aSdan       }
1086296c7658Sdan       sessionPutI64(aBuf, i);
10874fccf43aSdan       sessionAppendBlob(p, aBuf, 8, pRc);
10884fccf43aSdan     }
10894fccf43aSdan     if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){
10904fccf43aSdan       int nByte = sqlite3_column_bytes(pStmt, iCol);
10914fccf43aSdan       sessionAppendVarint(p, nByte, pRc);
10924fccf43aSdan       sessionAppendBlob(p, eType==SQLITE_BLOB ?
10934fccf43aSdan         sqlite3_column_blob(pStmt, iCol) : sqlite3_column_text(pStmt, iCol),
10944fccf43aSdan         nByte, pRc
10954fccf43aSdan       );
10964fccf43aSdan     }
10974fccf43aSdan   }
10984fccf43aSdan }
10994fccf43aSdan 
1100296c7658Sdan /*
1101296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1102296c7658Sdan ** called.
1103296c7658Sdan **
1104296c7658Sdan ** Otherwse, if *pRc is SQLITE_OK, then it appends an update change to
1105296c7658Sdan ** the buffer (see the comments under "CHANGESET FORMAT" at the top of the
1106296c7658Sdan ** file). An update change consists of:
1107296c7658Sdan **
1108296c7658Sdan **   1 byte:  SQLITE_UPDATE (0x17)
1109296c7658Sdan **   n bytes: old.* record (see RECORD FORMAT)
1110296c7658Sdan **   m bytes: new.* record (see RECORD FORMAT)
1111296c7658Sdan **
1112296c7658Sdan ** The SessionChange object passed as the third argument contains the
1113296c7658Sdan ** values that were stored in the row when the session began (the old.*
1114296c7658Sdan ** values). The statement handle passed as the second argument points
1115296c7658Sdan ** at the current version of the row (the new.* values).
1116296c7658Sdan **
1117296c7658Sdan ** If all of the old.* values are equal to their corresponding new.* value
1118296c7658Sdan ** (i.e. nothing has changed), then no data at all is appended to the buffer.
1119296c7658Sdan **
1120296c7658Sdan ** Otherwise, the old.* record contains all primary key values and the
1121296c7658Sdan ** original values of any fields that have been modified. The new.* record
1122296c7658Sdan ** contains the new values of only those fields that have been modified.
1123296c7658Sdan */
11244fccf43aSdan static void sessionAppendUpdate(
1125296c7658Sdan   SessionBuffer *pBuf,            /* Buffer to append to */
1126296c7658Sdan   sqlite3_stmt *pStmt,            /* Statement handle pointing at new row */
1127296c7658Sdan   SessionChange *p,               /* Object containing old values */
1128296c7658Sdan   u8 *abPK,                       /* Boolean array - true for PK columns */
1129296c7658Sdan   int *pRc                        /* IN/OUT: Error code */
11304fccf43aSdan ){
11314fccf43aSdan   if( *pRc==SQLITE_OK ){
1132296c7658Sdan     SessionBuffer buf2 = {0,0,0}; /* Buffer to accumulate new.* record in */
1133296c7658Sdan     int bNoop = 1;                /* Set to zero if any values are modified */
11341f34f8ccSdan     int nRewind = pBuf->nBuf;     /* Set to zero if any values are modified */
1135296c7658Sdan     int i;                        /* Used to iterate through columns */
1136296c7658Sdan     u8 *pCsr = p->aRecord;        /* Used to iterate through old.* values */
1137296c7658Sdan 
11384fccf43aSdan     sessionAppendByte(pBuf, SQLITE_UPDATE, pRc);
11394fccf43aSdan     for(i=0; i<sqlite3_column_count(pStmt); i++){
114037f133ecSdan       int bChanged = 0;
11414fccf43aSdan       int nAdvance;
11424fccf43aSdan       int eType = *pCsr;
11434fccf43aSdan       switch( eType ){
11444fccf43aSdan         case SQLITE_NULL:
11454fccf43aSdan           nAdvance = 1;
11464fccf43aSdan           if( sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){
114737f133ecSdan             bChanged = 1;
11484fccf43aSdan           }
11494fccf43aSdan           break;
11504fccf43aSdan 
11514fccf43aSdan         case SQLITE_FLOAT:
11524fccf43aSdan         case SQLITE_INTEGER: {
11534fccf43aSdan           nAdvance = 9;
11544fccf43aSdan           if( eType==sqlite3_column_type(pStmt, i) ){
11554fccf43aSdan             sqlite3_int64 iVal = sessionGetI64(&pCsr[1]);
11564fccf43aSdan             if( eType==SQLITE_INTEGER ){
11574fccf43aSdan               if( iVal==sqlite3_column_int64(pStmt, i) ) break;
11584fccf43aSdan             }else{
11594fccf43aSdan               double dVal;
11604fccf43aSdan               memcpy(&dVal, &iVal, 8);
11614fccf43aSdan               if( dVal==sqlite3_column_double(pStmt, i) ) break;
11624fccf43aSdan             }
11634fccf43aSdan           }
116437f133ecSdan           bChanged = 1;
11654fccf43aSdan           break;
11664fccf43aSdan         }
11674fccf43aSdan 
11684fccf43aSdan         case SQLITE_TEXT:
11694fccf43aSdan         case SQLITE_BLOB: {
11704fccf43aSdan           int nByte;
11714fccf43aSdan           int nHdr = 1 + sessionVarintGet(&pCsr[1], &nByte);
11724fccf43aSdan           nAdvance = nHdr + nByte;
11734fccf43aSdan           if( eType==sqlite3_column_type(pStmt, i)
11744fccf43aSdan            && nByte==sqlite3_column_bytes(pStmt, i)
11754fccf43aSdan            && 0==memcmp(&pCsr[nHdr], sqlite3_column_blob(pStmt, i), nByte)
11764fccf43aSdan           ){
11774fccf43aSdan             break;
11784fccf43aSdan           }
117937f133ecSdan           bChanged = 1;
11804fccf43aSdan         }
11814fccf43aSdan       }
11824fccf43aSdan 
118337f133ecSdan       if( bChanged || abPK[i] ){
118437f133ecSdan         sessionAppendBlob(pBuf, pCsr, nAdvance, pRc);
11854fccf43aSdan       }else{
118637f133ecSdan         sessionAppendByte(pBuf, 0, pRc);
118737f133ecSdan       }
118837f133ecSdan 
118937f133ecSdan       if( bChanged ){
11904fccf43aSdan         sessionAppendCol(&buf2, pStmt, i, pRc);
11914fccf43aSdan         bNoop = 0;
119237f133ecSdan       }else{
119337f133ecSdan         sessionAppendByte(&buf2, 0, pRc);
11944fccf43aSdan       }
119537f133ecSdan 
11964fccf43aSdan       pCsr += nAdvance;
11974fccf43aSdan     }
11984fccf43aSdan 
11994fccf43aSdan     if( bNoop ){
12001f34f8ccSdan       pBuf->nBuf = nRewind;
12014fccf43aSdan     }else{
12024fccf43aSdan       sessionAppendBlob(pBuf, buf2.aBuf, buf2.nBuf, pRc);
12034fccf43aSdan     }
12041f34f8ccSdan     sqlite3_free(buf2.aBuf);
12054fccf43aSdan   }
1206d5f0767cSdan }
12074fccf43aSdan 
1208e8d5648eSdan static int sessionSelectStmt(
1209e8d5648eSdan   sqlite3 *db,                    /* Database handle */
1210d7fb7d24Sdan   const char *zDb,                /* Database name */
1211e8d5648eSdan   const char *zTab,               /* Table name */
1212e8d5648eSdan   int nCol,
1213e8d5648eSdan   const char **azCol,
1214e8d5648eSdan   u8 *abPK,
1215e8d5648eSdan   sqlite3_stmt **ppStmt
1216d5f0767cSdan ){
1217e8d5648eSdan   int rc = SQLITE_OK;
1218d5f0767cSdan   int i;
1219e8d5648eSdan   const char *zSep = "";
1220e8d5648eSdan   SessionBuffer buf = {0, 0, 0};
1221d5f0767cSdan 
1222e8d5648eSdan   sessionAppendStr(&buf, "SELECT * FROM ", &rc);
1223d7fb7d24Sdan   sessionAppendIdent(&buf, zDb, &rc);
1224d7fb7d24Sdan   sessionAppendStr(&buf, ".", &rc);
1225e8d5648eSdan   sessionAppendIdent(&buf, zTab, &rc);
1226e8d5648eSdan   sessionAppendStr(&buf, " WHERE ", &rc);
1227e8d5648eSdan   for(i=0; i<nCol; i++){
1228e8d5648eSdan     if( abPK[i] ){
1229e8d5648eSdan       sessionAppendStr(&buf, zSep, &rc);
1230e8d5648eSdan       sessionAppendIdent(&buf, azCol[i], &rc);
1231e8d5648eSdan       sessionAppendStr(&buf, " = ?", &rc);
1232e8d5648eSdan       sessionAppendInteger(&buf, i+1, &rc);
1233e8d5648eSdan       zSep = " AND ";
1234d5f0767cSdan     }
1235d5f0767cSdan   }
1236d5f0767cSdan   if( rc==SQLITE_OK ){
1237e8d5648eSdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, ppStmt, 0);
1238d5f0767cSdan   }
1239e8d5648eSdan   sqlite3_free(buf.aBuf);
1240e8d5648eSdan   return rc;
1241d5f0767cSdan }
1242d5f0767cSdan 
1243e8d5648eSdan static int sessionSelectBind(
1244e8d5648eSdan   sqlite3_stmt *pSelect,
1245e8d5648eSdan   int nCol,
1246e8d5648eSdan   u8 *abPK,
1247e8d5648eSdan   u8 *aRecord,
1248e8d5648eSdan   int nRecord
1249e8d5648eSdan ){
1250e8d5648eSdan   int i;
1251e8d5648eSdan   int rc = SQLITE_OK;
1252e8d5648eSdan   u8 *a = aRecord;
1253d5f0767cSdan 
1254e8d5648eSdan   for(i=0; i<nCol && rc==SQLITE_OK; i++){
1255e8d5648eSdan     int eType = *a++;
1256e8d5648eSdan 
1257e8d5648eSdan     switch( eType ){
1258e8d5648eSdan       case SQLITE_NULL:
1259e8d5648eSdan         if( abPK[i] ) rc = sqlite3_bind_null(pSelect, i+1);
1260e8d5648eSdan         break;
1261e8d5648eSdan 
1262e8d5648eSdan       case SQLITE_INTEGER: {
1263e8d5648eSdan         if( abPK[i] ){
1264e8d5648eSdan           i64 iVal = sessionGetI64(a);
1265e8d5648eSdan           rc = sqlite3_bind_int64(pSelect, i+1, iVal);
1266e8d5648eSdan         }
1267e8d5648eSdan         a += 8;
1268e8d5648eSdan         break;
1269d5f0767cSdan       }
1270296c7658Sdan 
1271e8d5648eSdan       case SQLITE_FLOAT: {
1272e8d5648eSdan         if( abPK[i] ){
1273e8d5648eSdan           double rVal;
1274e8d5648eSdan           i64 iVal = sessionGetI64(a);
1275e8d5648eSdan           memcpy(&rVal, &iVal, 8);
12764e895da1Sdan           rc = sqlite3_bind_double(pSelect, i+1, rVal);
1277d5f0767cSdan         }
1278e8d5648eSdan         a += 8;
1279e8d5648eSdan         break;
1280e8d5648eSdan       }
1281e8d5648eSdan 
1282e8d5648eSdan       case SQLITE_TEXT: {
1283e8d5648eSdan         int n;
1284e8d5648eSdan         a += sessionVarintGet(a, &n);
1285e8d5648eSdan         if( abPK[i] ){
1286e8d5648eSdan           rc = sqlite3_bind_text(pSelect, i+1, (char *)a, n, SQLITE_TRANSIENT);
1287e8d5648eSdan         }
1288e8d5648eSdan         a += n;
1289e8d5648eSdan         break;
1290e8d5648eSdan       }
1291e8d5648eSdan 
1292e8d5648eSdan       case SQLITE_BLOB: {
1293e8d5648eSdan         int n;
1294e8d5648eSdan         a += sessionVarintGet(a, &n);
1295e8d5648eSdan         if( abPK[i] ){
1296e8d5648eSdan           rc = sqlite3_bind_blob(pSelect, i+1, a, n, SQLITE_TRANSIENT);
1297e8d5648eSdan         }
1298e8d5648eSdan         a += n;
1299e8d5648eSdan         break;
1300e8d5648eSdan       }
1301e8d5648eSdan     }
1302e8d5648eSdan   }
1303e8d5648eSdan 
1304d5f0767cSdan   return rc;
13054fccf43aSdan }
13064fccf43aSdan 
13074fccf43aSdan /*
13084fccf43aSdan ** Obtain a changeset object containing all changes recorded by the
13094fccf43aSdan ** session object passed as the first argument.
13104fccf43aSdan **
13114fccf43aSdan ** It is the responsibility of the caller to eventually free the buffer
13124fccf43aSdan ** using sqlite3_free().
13134fccf43aSdan */
13144fccf43aSdan int sqlite3session_changeset(
13154fccf43aSdan   sqlite3_session *pSession,      /* Session object */
13164fccf43aSdan   int *pnChangeset,               /* OUT: Size of buffer at *ppChangeset */
13174fccf43aSdan   void **ppChangeset              /* OUT: Buffer containing changeset */
13184fccf43aSdan ){
1319296c7658Sdan   sqlite3 *db = pSession->db;     /* Source database handle */
1320296c7658Sdan   SessionTable *pTab;             /* Used to iterate through attached tables */
1321296c7658Sdan   SessionBuffer buf = {0,0,0};    /* Buffer in which to accumlate changeset */
1322296c7658Sdan   int rc;                         /* Return code */
13234fccf43aSdan 
13244c220252Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
13254c220252Sdan 
1326296c7658Sdan   /* Zero the output variables in case an error occurs. If this session
1327296c7658Sdan   ** object is already in the error state (sqlite3_session.rc != SQLITE_OK),
1328296c7658Sdan   ** this call will be a no-op.  */
13294fccf43aSdan   *pnChangeset = 0;
13304fccf43aSdan   *ppChangeset = 0;
13314fccf43aSdan   rc = pSession->rc;
13324fccf43aSdan 
13334fccf43aSdan   for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
13344fccf43aSdan     if( pTab->nEntry ){
1335d7fb7d24Sdan       const char *zName = pTab->zName;
1336e8d5648eSdan       int nCol = pTab->nCol;      /* Local copy of member variable */
1337e8d5648eSdan       u8 *abPK = pTab->abPK;      /* Local copy of member variable */
13381f34f8ccSdan       int i;                      /* Used to iterate through hash buckets */
13391f34f8ccSdan       sqlite3_stmt *pSel = 0;     /* SELECT statement to query table pTab */
13401f34f8ccSdan       int nRewind = buf.nBuf;     /* Initial size of write buffer */
13411f34f8ccSdan       int nNoop;                  /* Size of buffer after writing tbl header */
13424fccf43aSdan 
13434fccf43aSdan       /* Write a table header */
13444fccf43aSdan       sessionAppendByte(&buf, 'T', &rc);
1345e8d5648eSdan       sessionAppendVarint(&buf, nCol, &rc);
1346d7fb7d24Sdan       sessionAppendBlob(&buf, (u8 *)zName, strlen(zName)+1, &rc);
13474fccf43aSdan 
13484fccf43aSdan       /* Build and compile a statement to execute: */
13494fccf43aSdan       if( rc==SQLITE_OK ){
1350d7fb7d24Sdan         rc = sessionSelectStmt(
1351d7fb7d24Sdan             db, pSession->zDb, zName, nCol, pTab->azCol, abPK, &pSel);
13524fccf43aSdan       }
13534fccf43aSdan 
13541f34f8ccSdan       if( rc==SQLITE_OK && nCol!=sqlite3_column_count(pSel) ){
13554fccf43aSdan         rc = SQLITE_SCHEMA;
13564fccf43aSdan       }
13574fccf43aSdan 
13581f34f8ccSdan       nNoop = buf.nBuf;
135912ca0b56Sdan       for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
1360e8d5648eSdan         SessionChange *p;         /* Used to iterate through changes */
1361e8d5648eSdan 
13624fccf43aSdan         for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
13631f34f8ccSdan           rc = sessionSelectBind(pSel, nCol, abPK, p->aRecord, p->nRecord);
1364e8d5648eSdan           if( rc==SQLITE_OK ){
13651f34f8ccSdan             if( sqlite3_step(pSel)==SQLITE_ROW ){
13664fccf43aSdan               int iCol;
1367e8d5648eSdan               if( p->bInsert ){
13684fccf43aSdan                 sessionAppendByte(&buf, SQLITE_INSERT, &rc);
1369e8d5648eSdan                 for(iCol=0; iCol<nCol; iCol++){
13701f34f8ccSdan                   sessionAppendCol(&buf, pSel, iCol, &rc);
13714fccf43aSdan                 }
1372e8d5648eSdan               }else{
13731f34f8ccSdan                 sessionAppendUpdate(&buf, pSel, p, abPK, &rc);
13744fccf43aSdan               }
1375e8d5648eSdan             }else if( !p->bInsert ){
13764fccf43aSdan               /* A DELETE change */
13774fccf43aSdan               sessionAppendByte(&buf, SQLITE_DELETE, &rc);
13784fccf43aSdan               sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
13794fccf43aSdan             }
138012ca0b56Sdan             if( rc==SQLITE_OK ){
13811f34f8ccSdan               rc = sqlite3_reset(pSel);
13824fccf43aSdan             }
13834fccf43aSdan           }
1384e8d5648eSdan         }
138512ca0b56Sdan       }
13864fccf43aSdan 
13871f34f8ccSdan       sqlite3_finalize(pSel);
13881f34f8ccSdan       if( buf.nBuf==nNoop ){
13894fccf43aSdan         buf.nBuf = nRewind;
13904fccf43aSdan       }
13914fccf43aSdan     }
13924fccf43aSdan   }
13934fccf43aSdan 
13944fccf43aSdan   if( rc==SQLITE_OK ){
13954fccf43aSdan     *pnChangeset = buf.nBuf;
13964fccf43aSdan     *ppChangeset = buf.aBuf;
13974fccf43aSdan   }else{
13984fccf43aSdan     sqlite3_free(buf.aBuf);
13994fccf43aSdan   }
14004c220252Sdan 
14014c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
14024fccf43aSdan   return rc;
14034fccf43aSdan }
14044fccf43aSdan 
1405296c7658Sdan /*
1406296c7658Sdan ** Enable or disable the session object passed as the first argument.
1407296c7658Sdan */
14084fccf43aSdan int sqlite3session_enable(sqlite3_session *pSession, int bEnable){
14094c220252Sdan   int ret;
14104c220252Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
1411296c7658Sdan   if( bEnable>=0 ){
1412296c7658Sdan     pSession->bEnable = bEnable;
14134fccf43aSdan   }
14144c220252Sdan   ret = pSession->bEnable;
14154c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
14164c220252Sdan   return ret;
1417296c7658Sdan }
14184fccf43aSdan 
14194fccf43aSdan /*
14204fccf43aSdan ** Create an iterator used to iterate through the contents of a changeset.
14214fccf43aSdan */
14224fccf43aSdan int sqlite3changeset_start(
1423296c7658Sdan   sqlite3_changeset_iter **pp,    /* OUT: Changeset iterator handle */
1424296c7658Sdan   int nChangeset,                 /* Size of buffer pChangeset in bytes */
1425296c7658Sdan   void *pChangeset                /* Pointer to buffer containing changeset */
14264fccf43aSdan ){
14274fccf43aSdan   sqlite3_changeset_iter *pRet;   /* Iterator to return */
14284fccf43aSdan   int nByte;                      /* Number of bytes to allocate for iterator */
14294fccf43aSdan 
1430296c7658Sdan   /* Zero the output variable in case an error occurs. */
1431296c7658Sdan   *pp = 0;
14324fccf43aSdan 
1433296c7658Sdan   /* Allocate and initialize the iterator structure. */
14344fccf43aSdan   nByte = sizeof(sqlite3_changeset_iter);
14354fccf43aSdan   pRet = (sqlite3_changeset_iter *)sqlite3_malloc(nByte);
14364fccf43aSdan   if( !pRet ) return SQLITE_NOMEM;
14374fccf43aSdan   memset(pRet, 0, sizeof(sqlite3_changeset_iter));
14384fccf43aSdan   pRet->aChangeset = (u8 *)pChangeset;
14394fccf43aSdan   pRet->nChangeset = nChangeset;
14404fccf43aSdan   pRet->pNext = pRet->aChangeset;
14414fccf43aSdan 
1442296c7658Sdan   /* Populate the output variable and return success. */
1443296c7658Sdan   *pp = pRet;
14444fccf43aSdan   return SQLITE_OK;
14454fccf43aSdan }
14464fccf43aSdan 
1447296c7658Sdan /*
1448296c7658Sdan ** Deserialize a single record from a buffer in memory. See "RECORD FORMAT"
1449296c7658Sdan ** for details.
1450296c7658Sdan **
1451296c7658Sdan ** When this function is called, *paChange points to the start of the record
1452296c7658Sdan ** to deserialize. Assuming no error occurs, *paChange is set to point to
1453296c7658Sdan ** one byte after the end of the same record before this function returns.
1454296c7658Sdan **
1455296c7658Sdan ** If successful, each element of the apOut[] array (allocated by the caller)
1456296c7658Sdan ** is set to point to an sqlite3_value object containing the value read
1457296c7658Sdan ** from the corresponding position in the record. If that value is not
1458296c7658Sdan ** included in the record (i.e. because the record is part of an UPDATE change
1459296c7658Sdan ** and the field was not modified), the corresponding element of apOut[] is
1460296c7658Sdan ** set to NULL.
1461296c7658Sdan **
1462296c7658Sdan ** It is the responsibility of the caller to free all sqlite_value structures
1463296c7658Sdan ** using sqlite3_free().
1464296c7658Sdan **
1465296c7658Sdan ** If an error occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
1466296c7658Sdan ** The apOut[] array may have been partially populated in this case.
1467296c7658Sdan */
14684fccf43aSdan static int sessionReadRecord(
14694fccf43aSdan   u8 **paChange,                  /* IN/OUT: Pointer to binary record */
14704fccf43aSdan   int nCol,                       /* Number of values in record */
14714fccf43aSdan   sqlite3_value **apOut           /* Write values to this array */
14724fccf43aSdan ){
1473296c7658Sdan   int i;                          /* Used to iterate through columns */
1474296c7658Sdan   u8 *aRec = *paChange;           /* Cursor for the serialized record */
14754fccf43aSdan 
14764fccf43aSdan   for(i=0; i<nCol; i++){
1477296c7658Sdan     int eType = *aRec++;          /* Type of value (SQLITE_NULL, TEXT etc.) */
147891ddd559Sdan     assert( !apOut || apOut[i]==0 );
14794fccf43aSdan     if( eType ){
148091ddd559Sdan       if( apOut ){
14814fccf43aSdan         apOut[i] = sqlite3ValueNew(0);
14824fccf43aSdan         if( !apOut[i] ) return SQLITE_NOMEM;
148391ddd559Sdan       }
14844fccf43aSdan 
14854fccf43aSdan       if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
14864fccf43aSdan         int nByte;
14874fccf43aSdan         int enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0);
14884fccf43aSdan         aRec += sessionVarintGet(aRec, &nByte);
148991ddd559Sdan         if( apOut ){
14904fccf43aSdan           sqlite3ValueSetStr(apOut[i], nByte, aRec, enc, SQLITE_STATIC);
149191ddd559Sdan         }
14924fccf43aSdan         aRec += nByte;
14934fccf43aSdan       }
14944fccf43aSdan       if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
149591ddd559Sdan         if( apOut ){
14964fccf43aSdan           sqlite3_int64 v = sessionGetI64(aRec);
14974fccf43aSdan           if( eType==SQLITE_INTEGER ){
14984fccf43aSdan             sqlite3VdbeMemSetInt64(apOut[i], v);
14994fccf43aSdan           }else{
15004fccf43aSdan             double d;
15014e895da1Sdan             memcpy(&d, &v, 8);
15024fccf43aSdan             sqlite3VdbeMemSetDouble(apOut[i], d);
15034fccf43aSdan           }
15044fccf43aSdan         }
150591ddd559Sdan         aRec += 8;
150691ddd559Sdan       }
15074fccf43aSdan     }
15084fccf43aSdan   }
15094fccf43aSdan 
15104fccf43aSdan   *paChange = aRec;
15114fccf43aSdan   return SQLITE_OK;
15124fccf43aSdan }
15134fccf43aSdan 
15144fccf43aSdan /*
15154fccf43aSdan ** Advance an iterator created by sqlite3changeset_start() to the next
15164fccf43aSdan ** change in the changeset. This function may return SQLITE_ROW, SQLITE_DONE
15174fccf43aSdan ** or SQLITE_CORRUPT.
15184fccf43aSdan **
15194fccf43aSdan ** This function may not be called on iterators passed to a conflict handler
15204fccf43aSdan ** callback by changeset_apply().
15214fccf43aSdan */
15224fccf43aSdan int sqlite3changeset_next(sqlite3_changeset_iter *p){
15234fccf43aSdan   u8 *aChange;
15244fccf43aSdan   int i;
15254fccf43aSdan   u8 c;
15264fccf43aSdan 
1527296c7658Sdan   /* If the iterator is in the error-state, return immediately. */
15284fccf43aSdan   if( p->rc!=SQLITE_OK ) return p->rc;
15294fccf43aSdan 
1530296c7658Sdan   /* Free the current contents of p->apValue[]. */
15314fccf43aSdan   if( p->apValue ){
15324fccf43aSdan     for(i=0; i<p->nCol*2; i++){
15334fccf43aSdan       sqlite3ValueFree(p->apValue[i]);
15344fccf43aSdan     }
15354fccf43aSdan     memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
15364fccf43aSdan   }
15374fccf43aSdan 
15384fccf43aSdan   /* If the iterator is already at the end of the changeset, return DONE. */
15394fccf43aSdan   if( p->pNext>=&p->aChangeset[p->nChangeset] ){
15404fccf43aSdan     return SQLITE_DONE;
15414fccf43aSdan   }
15424fccf43aSdan   aChange = p->pNext;
15434fccf43aSdan 
15444fccf43aSdan   c = *(aChange++);
15454fccf43aSdan   if( c=='T' ){
15464fccf43aSdan     int nByte;                    /* Bytes to allocate for apValue */
15474fccf43aSdan     aChange += sessionVarintGet(aChange, &p->nCol);
15484fccf43aSdan     p->zTab = (char *)aChange;
15494fccf43aSdan     aChange += (strlen((char *)aChange) + 1);
15504fccf43aSdan     p->op = *(aChange++);
15514fccf43aSdan     sqlite3_free(p->apValue);
15524fccf43aSdan     nByte = sizeof(sqlite3_value *) * p->nCol * 2;
15534fccf43aSdan     p->apValue = (sqlite3_value **)sqlite3_malloc(nByte);
15544fccf43aSdan     if( !p->apValue ){
15554fccf43aSdan       return (p->rc = SQLITE_NOMEM);
15564fccf43aSdan     }
15574fccf43aSdan     memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
15584fccf43aSdan   }else{
15594fccf43aSdan     p->op = c;
15604fccf43aSdan   }
15614fccf43aSdan   if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){
15624fccf43aSdan     return (p->rc = SQLITE_CORRUPT);
15634fccf43aSdan   }
15644fccf43aSdan 
15654fccf43aSdan   /* If this is an UPDATE or DELETE, read the old.* record. */
15664fccf43aSdan   if( p->op!=SQLITE_INSERT ){
15674fccf43aSdan     p->rc = sessionReadRecord(&aChange, p->nCol, p->apValue);
15684fccf43aSdan     if( p->rc!=SQLITE_OK ) return p->rc;
15694fccf43aSdan   }
15704fccf43aSdan 
15714fccf43aSdan   /* If this is an INSERT or UPDATE, read the new.* record. */
15724fccf43aSdan   if( p->op!=SQLITE_DELETE ){
15734fccf43aSdan     p->rc = sessionReadRecord(&aChange, p->nCol, &p->apValue[p->nCol]);
15744fccf43aSdan     if( p->rc!=SQLITE_OK ) return p->rc;
15754fccf43aSdan   }
15764fccf43aSdan 
15774fccf43aSdan   p->pNext = aChange;
15784fccf43aSdan   return SQLITE_ROW;
15794fccf43aSdan }
15804fccf43aSdan 
15814fccf43aSdan /*
15824fccf43aSdan ** The following three functions extract information on the current change
15834fccf43aSdan ** from a changeset iterator. They may only be called after changeset_next()
15844fccf43aSdan ** has returned SQLITE_ROW.
15854fccf43aSdan */
15864fccf43aSdan int sqlite3changeset_op(
1587296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Iterator handle */
15884fccf43aSdan   const char **pzTab,             /* OUT: Pointer to table name */
15894fccf43aSdan   int *pnCol,                     /* OUT: Number of columns in table */
15904fccf43aSdan   int *pOp                        /* OUT: SQLITE_INSERT, DELETE or UPDATE */
15914fccf43aSdan ){
15924fccf43aSdan   *pOp = pIter->op;
15934fccf43aSdan   *pnCol = pIter->nCol;
15944fccf43aSdan   *pzTab = pIter->zTab;
15954fccf43aSdan   return SQLITE_OK;
15964fccf43aSdan }
15974fccf43aSdan 
1598296c7658Sdan /*
1599296c7658Sdan ** This function may only be called while the iterator is pointing to an
1600296c7658Sdan ** SQLITE_UPDATE or SQLITE_DELETE change (see sqlite3changeset_op()).
1601296c7658Sdan ** Otherwise, SQLITE_MISUSE is returned.
1602296c7658Sdan **
1603296c7658Sdan ** It sets *ppValue to point to an sqlite3_value structure containing the
1604296c7658Sdan ** iVal'th value in the old.* record. Or, if that particular value is not
1605296c7658Sdan ** included in the record (because the change is an UPDATE and the field
1606296c7658Sdan ** was not modified and is not a PK column), set *ppValue to NULL.
1607296c7658Sdan **
1608296c7658Sdan ** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is
1609296c7658Sdan ** not modified. Otherwise, SQLITE_OK.
1610296c7658Sdan */
16114fccf43aSdan int sqlite3changeset_old(
1612296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
1613296c7658Sdan   int iVal,                       /* Index of old.* value to retrieve */
16144fccf43aSdan   sqlite3_value **ppValue         /* OUT: Old value (or NULL pointer) */
16154fccf43aSdan ){
1616d5f0767cSdan   if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_DELETE ){
1617d5f0767cSdan     return SQLITE_MISUSE;
1618d5f0767cSdan   }
16194fccf43aSdan   if( iVal<0 || iVal>=pIter->nCol ){
16204fccf43aSdan     return SQLITE_RANGE;
16214fccf43aSdan   }
16224fccf43aSdan   *ppValue = pIter->apValue[iVal];
16234fccf43aSdan   return SQLITE_OK;
16244fccf43aSdan }
16254fccf43aSdan 
1626296c7658Sdan /*
1627296c7658Sdan ** This function may only be called while the iterator is pointing to an
1628296c7658Sdan ** SQLITE_UPDATE or SQLITE_INSERT change (see sqlite3changeset_op()).
1629296c7658Sdan ** Otherwise, SQLITE_MISUSE is returned.
1630296c7658Sdan **
1631296c7658Sdan ** It sets *ppValue to point to an sqlite3_value structure containing the
1632296c7658Sdan ** iVal'th value in the new.* record. Or, if that particular value is not
1633296c7658Sdan ** included in the record (because the change is an UPDATE and the field
1634296c7658Sdan ** was not modified), set *ppValue to NULL.
1635296c7658Sdan **
1636296c7658Sdan ** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is
1637296c7658Sdan ** not modified. Otherwise, SQLITE_OK.
1638296c7658Sdan */
16394fccf43aSdan int sqlite3changeset_new(
1640296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
1641296c7658Sdan   int iVal,                       /* Index of new.* value to retrieve */
16424fccf43aSdan   sqlite3_value **ppValue         /* OUT: New value (or NULL pointer) */
16434fccf43aSdan ){
1644d5f0767cSdan   if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_INSERT ){
1645d5f0767cSdan     return SQLITE_MISUSE;
1646d5f0767cSdan   }
16474fccf43aSdan   if( iVal<0 || iVal>=pIter->nCol ){
16484fccf43aSdan     return SQLITE_RANGE;
16494fccf43aSdan   }
16504fccf43aSdan   *ppValue = pIter->apValue[pIter->nCol+iVal];
16514fccf43aSdan   return SQLITE_OK;
16524fccf43aSdan }
16534fccf43aSdan 
1654296c7658Sdan /*
16557aa469cdSdan ** The following two macros are used internally. They are similar to the
16567aa469cdSdan ** sqlite3changeset_new() and sqlite3changeset_old() functions, except that
16577aa469cdSdan ** they omit all error checking and return a pointer to the requested value.
16587aa469cdSdan */
16597aa469cdSdan #define sessionChangesetNew(pIter, iVal) (pIter)->apValue[(pIter)->nCol+(iVal)]
16607aa469cdSdan #define sessionChangesetOld(pIter, iVal) (pIter)->apValue[(iVal)]
16617aa469cdSdan 
16627aa469cdSdan /*
1663296c7658Sdan ** This function may only be called with a changeset iterator that has been
1664296c7658Sdan ** passed to an SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT
1665296c7658Sdan ** conflict-handler function. Otherwise, SQLITE_MISUSE is returned.
1666296c7658Sdan **
1667296c7658Sdan ** If successful, *ppValue is set to point to an sqlite3_value structure
1668296c7658Sdan ** containing the iVal'th value of the conflicting record.
1669296c7658Sdan **
1670296c7658Sdan ** If value iVal is out-of-range or some other error occurs, an SQLite error
1671296c7658Sdan ** code is returned. Otherwise, SQLITE_OK.
1672296c7658Sdan */
1673d5f0767cSdan int sqlite3changeset_conflict(
1674296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
1675296c7658Sdan   int iVal,                       /* Index of conflict record value to fetch */
1676d5f0767cSdan   sqlite3_value **ppValue         /* OUT: Value from conflicting row */
1677d5f0767cSdan ){
1678d5f0767cSdan   if( !pIter->pConflict ){
1679d5f0767cSdan     return SQLITE_MISUSE;
1680d5f0767cSdan   }
1681d5f0767cSdan   if( iVal<0 || iVal>=sqlite3_column_count(pIter->pConflict) ){
1682d5f0767cSdan     return SQLITE_RANGE;
1683d5f0767cSdan   }
1684d5f0767cSdan   *ppValue = sqlite3_column_value(pIter->pConflict, iVal);
1685d5f0767cSdan   return SQLITE_OK;
1686d5f0767cSdan }
1687d5f0767cSdan 
16884fccf43aSdan /*
16894fccf43aSdan ** Finalize an iterator allocated with sqlite3changeset_start().
16904fccf43aSdan **
16914fccf43aSdan ** This function may not be called on iterators passed to a conflict handler
16924fccf43aSdan ** callback by changeset_apply().
16934fccf43aSdan */
16944fccf43aSdan int sqlite3changeset_finalize(sqlite3_changeset_iter *p){
1695296c7658Sdan   int i;                          /* Used to iterate through p->apValue[] */
1696296c7658Sdan   int rc = p->rc;                 /* Return code */
169712ca0b56Sdan   if( p->apValue ){
16984fccf43aSdan     for(i=0; i<p->nCol*2; i++) sqlite3ValueFree(p->apValue[i]);
169912ca0b56Sdan   }
17004fccf43aSdan   sqlite3_free(p->apValue);
17014fccf43aSdan   sqlite3_free(p);
17024fccf43aSdan   return rc;
17034fccf43aSdan }
17044fccf43aSdan 
170591ddd559Sdan /*
170691ddd559Sdan ** Invert a changeset object.
170791ddd559Sdan */
170891ddd559Sdan int sqlite3changeset_invert(
170991ddd559Sdan   int nChangeset,                 /* Number of bytes in input */
171091ddd559Sdan   void *pChangeset,               /* Input changeset */
171191ddd559Sdan   int *pnInverted,                /* OUT: Number of bytes in output changeset */
171291ddd559Sdan   void **ppInverted               /* OUT: Inverse of pChangeset */
171391ddd559Sdan ){
171491ddd559Sdan   u8 *aOut;
171591ddd559Sdan   u8 *aIn;
171691ddd559Sdan   int i;
171791ddd559Sdan   int nCol = 0;
171891ddd559Sdan 
171991ddd559Sdan   /* Zero the output variables in case an error occurs. */
172091ddd559Sdan   *ppInverted = 0;
172191ddd559Sdan   *pnInverted = 0;
172291ddd559Sdan   if( nChangeset==0 ) return SQLITE_OK;
172391ddd559Sdan 
172491ddd559Sdan   aOut = (u8 *)sqlite3_malloc(nChangeset);
172591ddd559Sdan   if( !aOut ) return SQLITE_NOMEM;
172691ddd559Sdan   aIn = (u8 *)pChangeset;
172791ddd559Sdan 
172891ddd559Sdan   i = 0;
172991ddd559Sdan   while( i<nChangeset ){
173091ddd559Sdan     u8 eType = aIn[i];
173191ddd559Sdan     switch( eType ){
173291ddd559Sdan       case 'T': {
173391ddd559Sdan         int nByte = 1 + sessionVarintGet(&aIn[i+1], &nCol);
173491ddd559Sdan         nByte += 1 + strlen((char *)&aIn[i+nByte]);
173591ddd559Sdan         memcpy(&aOut[i], &aIn[i], nByte);
173691ddd559Sdan         i += nByte;
173791ddd559Sdan         break;
173891ddd559Sdan       }
173991ddd559Sdan 
174091ddd559Sdan       case SQLITE_INSERT:
174191ddd559Sdan       case SQLITE_DELETE: {
174291ddd559Sdan         int nByte;
174391ddd559Sdan         u8 *aEnd = &aIn[i+1];
174491ddd559Sdan 
174591ddd559Sdan         sessionReadRecord(&aEnd, nCol, 0);
174691ddd559Sdan         aOut[i] = (eType==SQLITE_DELETE ? SQLITE_INSERT : SQLITE_DELETE);
174791ddd559Sdan         nByte = aEnd - &aIn[i+1];
174891ddd559Sdan         memcpy(&aOut[i+1], &aIn[i+1], nByte);
174991ddd559Sdan         i += 1 + nByte;
175091ddd559Sdan         break;
175191ddd559Sdan       }
175291ddd559Sdan 
175391ddd559Sdan       case SQLITE_UPDATE: {
175491ddd559Sdan         int nByte1;              /* Size of old.* record in bytes */
175591ddd559Sdan         int nByte2;              /* Size of new.* record in bytes */
175691ddd559Sdan         u8 *aEnd = &aIn[i+1];
175791ddd559Sdan 
175891ddd559Sdan         sessionReadRecord(&aEnd, nCol, 0);
175991ddd559Sdan         nByte1 = aEnd - &aIn[i+1];
176091ddd559Sdan         sessionReadRecord(&aEnd, nCol, 0);
176191ddd559Sdan         nByte2 = aEnd - &aIn[i+1] - nByte1;
176291ddd559Sdan 
176391ddd559Sdan         aOut[i] = SQLITE_UPDATE;
176491ddd559Sdan         memcpy(&aOut[i+1], &aIn[i+1+nByte1], nByte2);
176591ddd559Sdan         memcpy(&aOut[i+1+nByte2], &aIn[i+1], nByte1);
176691ddd559Sdan 
176791ddd559Sdan         i += 1 + nByte1 + nByte2;
176891ddd559Sdan         break;
176991ddd559Sdan       }
177091ddd559Sdan 
177191ddd559Sdan       default:
177291ddd559Sdan         sqlite3_free(aOut);
177391ddd559Sdan         return SQLITE_CORRUPT;
177491ddd559Sdan     }
177591ddd559Sdan   }
177691ddd559Sdan 
177791ddd559Sdan   *pnInverted = nChangeset;
177891ddd559Sdan   *ppInverted = (void *)aOut;
177991ddd559Sdan   return SQLITE_OK;
178091ddd559Sdan }
178191ddd559Sdan 
17820c698471Sdan typedef struct SessionApplyCtx SessionApplyCtx;
17830c698471Sdan struct SessionApplyCtx {
17840c698471Sdan   sqlite3 *db;
17850c698471Sdan   sqlite3_stmt *pDelete;          /* DELETE statement */
17860c698471Sdan   sqlite3_stmt *pUpdate;          /* DELETE statement */
17870c698471Sdan   sqlite3_stmt *pInsert;          /* INSERT statement */
17880c698471Sdan   sqlite3_stmt *pSelect;          /* SELECT statement */
17890c698471Sdan   int nCol;                       /* Size of azCol[] and abPK[] arrays */
17900c698471Sdan   const char **azCol;             /* Array of column names */
17910c698471Sdan   u8 *abPK;                       /* Boolean array - true if column is in PK */
17920c698471Sdan };
17930c698471Sdan 
1794d5f0767cSdan /*
1795d5f0767cSdan ** Formulate a statement to DELETE a row from database db. Assuming a table
1796d5f0767cSdan ** structure like this:
1797d5f0767cSdan **
1798d5f0767cSdan **     CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
1799d5f0767cSdan **
1800d5f0767cSdan ** The DELETE statement looks like this:
1801d5f0767cSdan **
1802db04571cSdan **     DELETE FROM x WHERE a = :1 AND c = :3 AND (:5 OR b IS :2 AND d IS :4)
1803d5f0767cSdan **
1804d5f0767cSdan ** Variable :5 (nCol+1) is a boolean. It should be set to 0 if we require
1805d5f0767cSdan ** matching b and d values, or 1 otherwise. The second case comes up if the
1806d5f0767cSdan ** conflict handler is invoked with NOTFOUND and returns CHANGESET_REPLACE.
1807296c7658Sdan **
1808296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pDelete is left
1809296c7658Sdan ** pointing to the prepared version of the SQL statement.
1810d5f0767cSdan */
1811d5f0767cSdan static int sessionDeleteRow(
1812d5f0767cSdan   sqlite3 *db,                    /* Database handle */
1813d5f0767cSdan   const char *zTab,               /* Table name */
18140c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
1815d5f0767cSdan ){
1816296c7658Sdan   int i;
1817296c7658Sdan   const char *zSep = "";
1818d5f0767cSdan   int rc = SQLITE_OK;
1819d5f0767cSdan   SessionBuffer buf = {0, 0, 0};
18207cf7df7dSdan   int nPk = 0;
1821d5f0767cSdan 
1822d5f0767cSdan   sessionAppendStr(&buf, "DELETE FROM ", &rc);
1823d5f0767cSdan   sessionAppendIdent(&buf, zTab, &rc);
1824296c7658Sdan   sessionAppendStr(&buf, " WHERE ", &rc);
1825296c7658Sdan 
1826296c7658Sdan   for(i=0; i<p->nCol; i++){
1827296c7658Sdan     if( p->abPK[i] ){
18287cf7df7dSdan       nPk++;
1829296c7658Sdan       sessionAppendStr(&buf, zSep, &rc);
1830296c7658Sdan       sessionAppendIdent(&buf, p->azCol[i], &rc);
1831296c7658Sdan       sessionAppendStr(&buf, " = ?", &rc);
1832296c7658Sdan       sessionAppendInteger(&buf, i+1, &rc);
1833296c7658Sdan       zSep = " AND ";
1834296c7658Sdan     }
1835296c7658Sdan   }
1836296c7658Sdan 
18377cf7df7dSdan   if( nPk<p->nCol ){
1838296c7658Sdan     sessionAppendStr(&buf, " AND (?", &rc);
1839296c7658Sdan     sessionAppendInteger(&buf, p->nCol+1, &rc);
1840296c7658Sdan     sessionAppendStr(&buf, " OR ", &rc);
1841296c7658Sdan 
1842296c7658Sdan     zSep = "";
1843296c7658Sdan     for(i=0; i<p->nCol; i++){
1844296c7658Sdan       if( !p->abPK[i] ){
1845296c7658Sdan         sessionAppendStr(&buf, zSep, &rc);
1846296c7658Sdan         sessionAppendIdent(&buf, p->azCol[i], &rc);
1847296c7658Sdan         sessionAppendStr(&buf, " IS ?", &rc);
1848296c7658Sdan         sessionAppendInteger(&buf, i+1, &rc);
1849296c7658Sdan         zSep = "AND ";
1850296c7658Sdan       }
1851296c7658Sdan     }
1852296c7658Sdan     sessionAppendStr(&buf, ")", &rc);
18537cf7df7dSdan   }
1854d5f0767cSdan 
1855d5f0767cSdan   if( rc==SQLITE_OK ){
18560c698471Sdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pDelete, 0);
1857d5f0767cSdan   }
1858d5f0767cSdan   sqlite3_free(buf.aBuf);
1859d5f0767cSdan 
1860d5f0767cSdan   return rc;
1861d5f0767cSdan }
1862d5f0767cSdan 
1863d5f0767cSdan /*
1864d5f0767cSdan ** Formulate and prepare a statement to UPDATE a row from database db.
1865d5f0767cSdan ** Assuming a table structure like this:
1866d5f0767cSdan **
1867d5f0767cSdan **     CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
1868d5f0767cSdan **
1869d5f0767cSdan ** The UPDATE statement looks like this:
1870d5f0767cSdan **
1871d5f0767cSdan **     UPDATE x SET
1872d5f0767cSdan **     a = CASE WHEN ?2  THEN ?3  ELSE a END,
1873d5f0767cSdan **     b = CASE WHEN ?5  THEN ?6  ELSE a END,
1874d5f0767cSdan **     c = CASE WHEN ?8  THEN ?9  ELSE a END,
1875d5f0767cSdan **     d = CASE WHEN ?11 THEN ?12 ELSE a END
1876d5f0767cSdan **     WHERE a = ?1 AND c = ?7 AND (?13 OR
1877d5f0767cSdan **       (?5==0 OR b IS ?4) AND (?11==0 OR b IS ?10) AND
1878d5f0767cSdan **     )
1879d5f0767cSdan **
1880d5f0767cSdan ** For each column in the table, there are three variables to bind:
1881d5f0767cSdan **
1882d5f0767cSdan **     ?(i*3+1)    The old.* value of the column, if any.
1883d5f0767cSdan **     ?(i*3+2)    A boolean flag indicating that the value is being modified.
1884d5f0767cSdan **     ?(i*3+3)    The new.* value of the column, if any.
1885d5f0767cSdan **
1886d5f0767cSdan ** Also, a boolean flag that, if set to true, causes the statement to update
1887d5f0767cSdan ** a row even if the non-PK values do not match. This is required if the
1888d5f0767cSdan ** conflict-handler is invoked with CHANGESET_DATA and returns
1889d5f0767cSdan ** CHANGESET_REPLACE. This is variable "?(nCol*3+1)".
1890d5f0767cSdan **
1891296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left
1892296c7658Sdan ** pointing to the prepared version of the SQL statement.
1893d5f0767cSdan */
1894d5f0767cSdan static int sessionUpdateRow(
1895d5f0767cSdan   sqlite3 *db,                    /* Database handle */
1896d5f0767cSdan   const char *zTab,               /* Table name */
18970c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
1898d5f0767cSdan ){
1899d5f0767cSdan   int rc = SQLITE_OK;
1900d5f0767cSdan   int i;
1901d5f0767cSdan   const char *zSep = "";
1902d5f0767cSdan   SessionBuffer buf = {0, 0, 0};
1903d5f0767cSdan 
1904d5f0767cSdan   /* Append "UPDATE tbl SET " */
1905d5f0767cSdan   sessionAppendStr(&buf, "UPDATE ", &rc);
1906d5f0767cSdan   sessionAppendIdent(&buf, zTab, &rc);
1907d5f0767cSdan   sessionAppendStr(&buf, " SET ", &rc);
1908d5f0767cSdan 
1909d5f0767cSdan   /* Append the assignments */
19100c698471Sdan   for(i=0; i<p->nCol; i++){
1911d5f0767cSdan     sessionAppendStr(&buf, zSep, &rc);
19120c698471Sdan     sessionAppendIdent(&buf, p->azCol[i], &rc);
1913d5f0767cSdan     sessionAppendStr(&buf, " = CASE WHEN ?", &rc);
1914d5f0767cSdan     sessionAppendInteger(&buf, i*3+2, &rc);
1915d5f0767cSdan     sessionAppendStr(&buf, " THEN ?", &rc);
1916d5f0767cSdan     sessionAppendInteger(&buf, i*3+3, &rc);
1917d5f0767cSdan     sessionAppendStr(&buf, " ELSE ", &rc);
19180c698471Sdan     sessionAppendIdent(&buf, p->azCol[i], &rc);
1919d5f0767cSdan     sessionAppendStr(&buf, " END", &rc);
1920d5f0767cSdan     zSep = ", ";
1921d5f0767cSdan   }
1922d5f0767cSdan 
1923d5f0767cSdan   /* Append the PK part of the WHERE clause */
1924d5f0767cSdan   sessionAppendStr(&buf, " WHERE ", &rc);
19250c698471Sdan   for(i=0; i<p->nCol; i++){
19260c698471Sdan     if( p->abPK[i] ){
19270c698471Sdan       sessionAppendIdent(&buf, p->azCol[i], &rc);
1928d5f0767cSdan       sessionAppendStr(&buf, " = ?", &rc);
1929d5f0767cSdan       sessionAppendInteger(&buf, i*3+1, &rc);
1930d5f0767cSdan       sessionAppendStr(&buf, " AND ", &rc);
1931d5f0767cSdan     }
1932d5f0767cSdan   }
1933d5f0767cSdan 
1934d5f0767cSdan   /* Append the non-PK part of the WHERE clause */
1935d5f0767cSdan   sessionAppendStr(&buf, " (?", &rc);
19360c698471Sdan   sessionAppendInteger(&buf, p->nCol*3+1, &rc);
1937d5f0767cSdan   sessionAppendStr(&buf, " OR 1", &rc);
19380c698471Sdan   for(i=0; i<p->nCol; i++){
19390c698471Sdan     if( !p->abPK[i] ){
1940d5f0767cSdan       sessionAppendStr(&buf, " AND (?", &rc);
1941d5f0767cSdan       sessionAppendInteger(&buf, i*3+2, &rc);
1942d5f0767cSdan       sessionAppendStr(&buf, "=0 OR ", &rc);
19430c698471Sdan       sessionAppendIdent(&buf, p->azCol[i], &rc);
1944d5f0767cSdan       sessionAppendStr(&buf, " IS ?", &rc);
1945d5f0767cSdan       sessionAppendInteger(&buf, i*3+1, &rc);
1946d5f0767cSdan       sessionAppendStr(&buf, ")", &rc);
1947d5f0767cSdan     }
1948d5f0767cSdan   }
1949d5f0767cSdan   sessionAppendStr(&buf, ")", &rc);
1950d5f0767cSdan 
1951d5f0767cSdan   if( rc==SQLITE_OK ){
19520c698471Sdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0);
1953d5f0767cSdan   }
1954d5f0767cSdan   sqlite3_free(buf.aBuf);
1955d5f0767cSdan 
1956d5f0767cSdan   return rc;
1957d5f0767cSdan }
1958d5f0767cSdan 
1959296c7658Sdan /*
1960296c7658Sdan ** Formulate and prepare an SQL statement to query table zTab by primary
1961296c7658Sdan ** key. Assuming the following table structure:
1962296c7658Sdan **
1963296c7658Sdan **     CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
1964296c7658Sdan **
1965296c7658Sdan ** The SELECT statement looks like this:
1966296c7658Sdan **
1967296c7658Sdan **     SELECT * FROM x WHERE a = ?1 AND c = ?3
1968296c7658Sdan **
1969296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pSelect is left
1970296c7658Sdan ** pointing to the prepared version of the SQL statement.
1971296c7658Sdan */
1972d5f0767cSdan static int sessionSelectRow(
1973d5f0767cSdan   sqlite3 *db,                    /* Database handle */
1974d5f0767cSdan   const char *zTab,               /* Table name */
19750c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
1976d5f0767cSdan ){
1977d7fb7d24Sdan   return sessionSelectStmt(
1978d7fb7d24Sdan       db, "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect);
1979d5f0767cSdan }
1980d5f0767cSdan 
1981296c7658Sdan /*
1982296c7658Sdan ** Formulate and prepare an INSERT statement to add a record to table zTab.
1983296c7658Sdan ** For example:
1984296c7658Sdan **
1985296c7658Sdan **     INSERT INTO main."zTab" VALUES(?1, ?2, ?3 ...);
1986296c7658Sdan **
1987296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pInsert is left
1988296c7658Sdan ** pointing to the prepared version of the SQL statement.
1989296c7658Sdan */
19900c698471Sdan static int sessionInsertRow(
19910c698471Sdan   sqlite3 *db,                    /* Database handle */
19920c698471Sdan   const char *zTab,               /* Table name */
19930c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
19940c698471Sdan ){
19950c698471Sdan   int rc = SQLITE_OK;
19960c698471Sdan   int i;
19970c698471Sdan   SessionBuffer buf = {0, 0, 0};
19980c698471Sdan 
19990c698471Sdan   sessionAppendStr(&buf, "INSERT INTO main.", &rc);
20000c698471Sdan   sessionAppendIdent(&buf, zTab, &rc);
20010c698471Sdan   sessionAppendStr(&buf, " VALUES(?", &rc);
20020c698471Sdan   for(i=1; i<p->nCol; i++){
20030c698471Sdan     sessionAppendStr(&buf, ", ?", &rc);
20040c698471Sdan   }
20050c698471Sdan   sessionAppendStr(&buf, ")", &rc);
20060c698471Sdan 
20070c698471Sdan   if( rc==SQLITE_OK ){
20080c698471Sdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pInsert, 0);
20090c698471Sdan   }
20100c698471Sdan   sqlite3_free(buf.aBuf);
20110c698471Sdan   return rc;
20120c698471Sdan }
20130c698471Sdan 
2014296c7658Sdan /*
20157aa469cdSdan ** A wrapper around sqlite3_bind_value() that detects an extra problem.
20167aa469cdSdan ** See comments in the body of this function for details.
20177aa469cdSdan */
20187aa469cdSdan static int sessionBindValue(
20197aa469cdSdan   sqlite3_stmt *pStmt,            /* Statement to bind value to */
20207aa469cdSdan   int i,                          /* Parameter number to bind to */
20217aa469cdSdan   sqlite3_value *pVal             /* Value to bind */
20227aa469cdSdan ){
20237aa469cdSdan   if( (pVal->type==SQLITE_TEXT || pVal->type==SQLITE_BLOB) && pVal->z==0 ){
20247aa469cdSdan     /* This condition occurs when an earlier OOM in a call to
20257aa469cdSdan     ** sqlite3_value_text() or sqlite3_value_blob() (perhaps from within
20267aa469cdSdan     ** a conflict-hanler) has zeroed the pVal->z pointer. Return NOMEM. */
20277aa469cdSdan     return SQLITE_NOMEM;
20287aa469cdSdan   }
20297aa469cdSdan   return sqlite3_bind_value(pStmt, i, pVal);
20307aa469cdSdan }
20317aa469cdSdan 
20327aa469cdSdan /*
2033db04571cSdan ** Iterator pIter must point to an SQLITE_INSERT entry. This function
2034db04571cSdan ** transfers new.* values from the current iterator entry to statement
2035db04571cSdan ** pStmt. The table being inserted into has nCol columns.
2036db04571cSdan **
2037db04571cSdan ** New.* value $i 0 from the iterator is bound to variable ($i+1) of
2038db04571cSdan ** statement pStmt. If parameter abPK is NULL, all values from 0 to (nCol-1)
2039db04571cSdan ** are transfered to the statement. Otherwise, if abPK is not NULL, it points
2040db04571cSdan ** to an array nCol elements in size. In this case only those values for
2041db04571cSdan ** which abPK[$i] is true are read from the iterator and bound to the
2042db04571cSdan ** statement.
2043db04571cSdan **
2044db04571cSdan ** An SQLite error code is returned if an error occurs. Otherwise, SQLITE_OK.
2045db04571cSdan */
20467aa469cdSdan static int sessionBindRow(
2047db04571cSdan   sqlite3_changeset_iter *pIter,  /* Iterator to read values from */
20487aa469cdSdan   int(*xValue)(sqlite3_changeset_iter *, int, sqlite3_value **),
2049db04571cSdan   int nCol,                       /* Number of columns */
2050db04571cSdan   u8 *abPK,                       /* If not NULL, bind only if true */
2051db04571cSdan   sqlite3_stmt *pStmt             /* Bind values to this statement */
2052db04571cSdan ){
2053db04571cSdan   int i;
2054db04571cSdan   int rc = SQLITE_OK;
20557aa469cdSdan 
20567aa469cdSdan   /* Neither sqlite3changeset_old or sqlite3changeset_new can fail if the
20577aa469cdSdan   ** argument iterator points to a suitable entry. Make sure that xValue
20587aa469cdSdan   ** is one of these to guarantee that it is safe to ignore the return
20597aa469cdSdan   ** in the code below. */
20607aa469cdSdan   assert( xValue==sqlite3changeset_old || xValue==sqlite3changeset_new );
20617aa469cdSdan 
2062db04571cSdan   for(i=0; rc==SQLITE_OK && i<nCol; i++){
2063db04571cSdan     if( !abPK || abPK[i] ){
2064db04571cSdan       sqlite3_value *pVal;
20657aa469cdSdan       (void)xValue(pIter, i, &pVal);
20667aa469cdSdan       rc = sessionBindValue(pStmt, i+1, pVal);
2067db04571cSdan     }
2068db04571cSdan   }
2069db04571cSdan   return rc;
2070db04571cSdan }
2071db04571cSdan 
2072db04571cSdan /*
2073296c7658Sdan ** SQL statement pSelect is as generated by the sessionSelectRow() function.
2074296c7658Sdan ** This function binds the primary key values from the change that changeset
2075296c7658Sdan ** iterator pIter points to to the SELECT and attempts to seek to the table
2076296c7658Sdan ** entry. If a row is found, the SELECT statement left pointing at the row
2077296c7658Sdan ** and SQLITE_ROW is returned. Otherwise, if no row is found and no error
2078296c7658Sdan ** has occured, the statement is reset and SQLITE_OK is returned. If an
20797aa469cdSdan ** error occurs, the statement is reset and an SQLite error code is returned.
20807aa469cdSdan **
20817aa469cdSdan ** If this function returns SQLITE_ROW, the caller must eventually reset()
20827aa469cdSdan ** statement pSelect. If any other value is returned, the statement does
20837aa469cdSdan ** not require a reset().
2084296c7658Sdan **
2085296c7658Sdan ** If the iterator currently points to an INSERT record, bind values from the
2086db04571cSdan ** new.* record to the SELECT statement. Or, if it points to a DELETE or
2087db04571cSdan ** UPDATE, bind values from the old.* record.
2088296c7658Sdan */
20890c698471Sdan static int sessionSeekToRow(
209037f133ecSdan   sqlite3 *db,                    /* Database handle */
209137f133ecSdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
209237f133ecSdan   u8 *abPK,                       /* Primary key flags array */
20930c698471Sdan   sqlite3_stmt *pSelect           /* SELECT statement from sessionSelectRow() */
209437f133ecSdan ){
20957aa469cdSdan   int rc;                         /* Return code */
2096296c7658Sdan   int nCol;                       /* Number of columns in table */
2097296c7658Sdan   int op;                         /* Changset operation (SQLITE_UPDATE etc.) */
2098296c7658Sdan   const char *zDummy;             /* Unused */
209937f133ecSdan 
210037f133ecSdan   sqlite3changeset_op(pIter, &zDummy, &nCol, &op);
21017aa469cdSdan   rc = sessionBindRow(pIter,
2102db04571cSdan       op==SQLITE_INSERT ? sqlite3changeset_new : sqlite3changeset_old,
2103db04571cSdan       nCol, abPK, pSelect
2104db04571cSdan   );
21050c698471Sdan 
21060c698471Sdan   if( rc==SQLITE_OK ){
21070c698471Sdan     rc = sqlite3_step(pSelect);
21080c698471Sdan     if( rc!=SQLITE_ROW ) rc = sqlite3_reset(pSelect);
21090c698471Sdan   }
21100c698471Sdan 
21110c698471Sdan   return rc;
21120c698471Sdan }
21130c698471Sdan 
2114296c7658Sdan /*
2115296c7658Sdan ** Invoke the conflict handler for the change that the changeset iterator
2116296c7658Sdan ** currently points to.
2117296c7658Sdan **
2118296c7658Sdan ** Argument eType must be either CHANGESET_DATA or CHANGESET_CONFLICT.
2119296c7658Sdan ** If argument pbReplace is NULL, then the type of conflict handler invoked
2120296c7658Sdan ** depends solely on eType, as follows:
2121296c7658Sdan **
2122296c7658Sdan **    eType value                 Value passed to xConflict
2123296c7658Sdan **    -------------------------------------------------
2124296c7658Sdan **    CHANGESET_DATA              CHANGESET_NOTFOUND
2125296c7658Sdan **    CHANGESET_CONFLICT          CHANGESET_CONSTRAINT
2126296c7658Sdan **
2127296c7658Sdan ** Or, if pbReplace is not NULL, then an attempt is made to find an existing
2128296c7658Sdan ** record with the same primary key as the record about to be deleted, updated
2129296c7658Sdan ** or inserted. If such a record can be found, it is available to the conflict
2130296c7658Sdan ** handler as the "conflicting" record. In this case the type of conflict
2131296c7658Sdan ** handler invoked is as follows:
2132296c7658Sdan **
2133296c7658Sdan **    eType value         PK Record found?   Value passed to xConflict
2134296c7658Sdan **    ----------------------------------------------------------------
2135296c7658Sdan **    CHANGESET_DATA      Yes                CHANGESET_DATA
2136296c7658Sdan **    CHANGESET_DATA      No                 CHANGESET_NOTFOUND
2137296c7658Sdan **    CHANGESET_CONFLICT  Yes                CHANGESET_CONFLICT
2138296c7658Sdan **    CHANGESET_CONFLICT  No                 CHANGESET_CONSTRAINT
2139296c7658Sdan **
2140296c7658Sdan ** If pbReplace is not NULL, and a record with a matching PK is found, and
2141296c7658Sdan ** the conflict handler function returns SQLITE_CHANGESET_REPLACE, *pbReplace
2142296c7658Sdan ** is set to non-zero before returning SQLITE_OK.
2143296c7658Sdan **
2144296c7658Sdan ** If the conflict handler returns SQLITE_CHANGESET_ABORT, SQLITE_ABORT is
2145296c7658Sdan ** returned. Or, if the conflict handler returns an invalid value,
2146296c7658Sdan ** SQLITE_MISUSE. If the conflict handler returns SQLITE_CHANGESET_OMIT,
2147296c7658Sdan ** this function returns SQLITE_OK.
2148296c7658Sdan */
21490c698471Sdan static int sessionConflictHandler(
2150296c7658Sdan   int eType,                      /* Either CHANGESET_DATA or CONFLICT */
2151296c7658Sdan   SessionApplyCtx *p,             /* changeset_apply() context */
21520c698471Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
21530c698471Sdan   int(*xConflict)(void *, int, sqlite3_changeset_iter*),
2154296c7658Sdan   void *pCtx,                     /* First argument for conflict handler */
2155296c7658Sdan   int *pbReplace                  /* OUT: Set to true if PK row is found */
21560c698471Sdan ){
2157296c7658Sdan   int res;                        /* Value returned by conflict handler */
21580c698471Sdan   int rc;
21590c698471Sdan   int nCol;
21600c698471Sdan   int op;
21610c698471Sdan   const char *zDummy;
21620c698471Sdan 
21630c698471Sdan   sqlite3changeset_op(pIter, &zDummy, &nCol, &op);
21640c698471Sdan 
21650c698471Sdan   assert( eType==SQLITE_CHANGESET_CONFLICT || eType==SQLITE_CHANGESET_DATA );
21660c698471Sdan   assert( SQLITE_CHANGESET_CONFLICT+1==SQLITE_CHANGESET_CONSTRAINT );
21670c698471Sdan   assert( SQLITE_CHANGESET_DATA+1==SQLITE_CHANGESET_NOTFOUND );
216837f133ecSdan 
216937f133ecSdan   /* Bind the new.* PRIMARY KEY values to the SELECT statement. */
21700c698471Sdan   if( pbReplace ){
21710c698471Sdan     rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect);
21720c698471Sdan   }else{
2173db04571cSdan     rc = SQLITE_OK;
21740c698471Sdan   }
21750c698471Sdan 
21760c698471Sdan   if( rc==SQLITE_ROW ){
21770c698471Sdan     /* There exists another row with the new.* primary key. */
21780c698471Sdan     pIter->pConflict = p->pSelect;
21790c698471Sdan     res = xConflict(pCtx, eType, pIter);
21800c698471Sdan     pIter->pConflict = 0;
21810c698471Sdan     rc = sqlite3_reset(p->pSelect);
2182db04571cSdan   }else if( rc==SQLITE_OK ){
21830c698471Sdan     /* No other row with the new.* primary key. */
21840c698471Sdan     res = xConflict(pCtx, eType+1, pIter);
21850c698471Sdan     if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE;
218637f133ecSdan   }
218737f133ecSdan 
218837f133ecSdan   if( rc==SQLITE_OK ){
21890c698471Sdan     switch( res ){
21900c698471Sdan       case SQLITE_CHANGESET_REPLACE:
21910c698471Sdan         if( pbReplace ) *pbReplace = 1;
21920c698471Sdan         break;
21930c698471Sdan 
21940c698471Sdan       case SQLITE_CHANGESET_OMIT:
21950c698471Sdan         break;
21960c698471Sdan 
21970c698471Sdan       case SQLITE_CHANGESET_ABORT:
21980c698471Sdan         rc = SQLITE_ABORT;
21990c698471Sdan         break;
22000c698471Sdan 
22010c698471Sdan       default:
22020c698471Sdan         rc = SQLITE_MISUSE;
22030c698471Sdan         break;
22040c698471Sdan     }
22050c698471Sdan   }
22060c698471Sdan 
22070c698471Sdan   return rc;
22080c698471Sdan }
22090c698471Sdan 
2210296c7658Sdan /*
2211296c7658Sdan ** Attempt to apply the change that the iterator passed as the first argument
2212296c7658Sdan ** currently points to to the database. If a conflict is encountered, invoke
2213296c7658Sdan ** the conflict handler callback.
2214296c7658Sdan **
2215296c7658Sdan ** If argument pbRetry is NULL, then ignore any CHANGESET_DATA conflict. If
2216296c7658Sdan ** one is encountered, update or delete the row with the matching primary key
2217296c7658Sdan ** instead. Or, if pbRetry is not NULL and a CHANGESET_DATA conflict occurs,
2218296c7658Sdan ** invoke the conflict handler. If it returns CHANGESET_REPLACE, set *pbRetry
2219296c7658Sdan ** to true before returning. In this case the caller will invoke this function
2220296c7658Sdan ** again, this time with pbRetry set to NULL.
2221296c7658Sdan **
2222296c7658Sdan ** If argument pbReplace is NULL and a CHANGESET_CONFLICT conflict is
2223296c7658Sdan ** encountered invoke the conflict handler with CHANGESET_CONSTRAINT instead.
2224296c7658Sdan ** Or, if pbReplace is not NULL, invoke it with CHANGESET_CONFLICT. If such
2225296c7658Sdan ** an invocation returns SQLITE_CHANGESET_REPLACE, set *pbReplace to true
2226296c7658Sdan ** before retrying. In this case the caller attempts to remove the conflicting
2227296c7658Sdan ** row before invoking this function again, this time with pbReplace set
2228296c7658Sdan ** to NULL.
2229296c7658Sdan **
2230296c7658Sdan ** If any conflict handler returns SQLITE_CHANGESET_ABORT, this function
2231296c7658Sdan ** returns SQLITE_ABORT. Otherwise, if no error occurs, SQLITE_OK is
2232296c7658Sdan ** returned.
2233296c7658Sdan */
22340c698471Sdan static int sessionApplyOneOp(
2235296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
2236296c7658Sdan   SessionApplyCtx *p,             /* changeset_apply() context */
22370c698471Sdan   int(*xConflict)(void *, int, sqlite3_changeset_iter *),
2238296c7658Sdan   void *pCtx,                     /* First argument for the conflict handler */
2239296c7658Sdan   int *pbReplace,                 /* OUT: True to remove PK row and retry */
2240296c7658Sdan   int *pbRetry                    /* OUT: True to retry. */
22410c698471Sdan ){
22420c698471Sdan   const char *zDummy;
22430c698471Sdan   int op;
22440c698471Sdan   int nCol;
22450c698471Sdan   int rc = SQLITE_OK;
22460c698471Sdan 
22470c698471Sdan   assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect );
22480c698471Sdan   assert( p->azCol && p->abPK );
22490c698471Sdan   assert( !pbReplace || *pbReplace==0 );
22500c698471Sdan 
22510c698471Sdan   sqlite3changeset_op(pIter, &zDummy, &nCol, &op);
22520c698471Sdan 
22530c698471Sdan   if( op==SQLITE_DELETE ){
22540c698471Sdan 
22550c698471Sdan     /* Bind values to the DELETE statement. */
22567aa469cdSdan     rc = sessionBindRow(pIter, sqlite3changeset_old, nCol, 0, p->pDelete);
22577cf7df7dSdan     if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){
22587cf7df7dSdan       rc = sqlite3_bind_int(p->pDelete, nCol+1, pbRetry==0);
22597cf7df7dSdan     }
22600c698471Sdan     if( rc!=SQLITE_OK ) return rc;
22610c698471Sdan 
22620c698471Sdan     sqlite3_step(p->pDelete);
22630c698471Sdan     rc = sqlite3_reset(p->pDelete);
22640c698471Sdan     if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
22650c698471Sdan       rc = sessionConflictHandler(
22660c698471Sdan           SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
22670c698471Sdan       );
22680c698471Sdan     }else if( rc==SQLITE_CONSTRAINT ){
22690c698471Sdan       rc = sessionConflictHandler(
22700c698471Sdan           SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
22710c698471Sdan       );
22720c698471Sdan     }
22730c698471Sdan 
22740c698471Sdan   }else if( op==SQLITE_UPDATE ){
22750c698471Sdan     int i;
22760c698471Sdan 
22770c698471Sdan     /* Bind values to the UPDATE statement. */
22780c698471Sdan     for(i=0; rc==SQLITE_OK && i<nCol; i++){
22797aa469cdSdan       sqlite3_value *pOld = sessionChangesetOld(pIter, i);
22807aa469cdSdan       sqlite3_value *pNew = sessionChangesetNew(pIter, i);
22817aa469cdSdan 
22820c698471Sdan       sqlite3_bind_int(p->pUpdate, i*3+2, !!pNew);
22837aa469cdSdan       if( pOld ){
22847aa469cdSdan         rc = sessionBindValue(p->pUpdate, i*3+1, pOld);
22857aa469cdSdan       }
22867aa469cdSdan       if( rc==SQLITE_OK && pNew ){
22877aa469cdSdan         rc = sessionBindValue(p->pUpdate, i*3+3, pNew);
22880c698471Sdan       }
22890c698471Sdan     }
22907aa469cdSdan     if( rc==SQLITE_OK ) sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0);
22910c698471Sdan     if( rc!=SQLITE_OK ) return rc;
22920c698471Sdan 
22930c698471Sdan     /* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict,
22940c698471Sdan     ** the result will be SQLITE_OK with 0 rows modified. */
22950c698471Sdan     sqlite3_step(p->pUpdate);
22960c698471Sdan     rc = sqlite3_reset(p->pUpdate);
22970c698471Sdan 
22980c698471Sdan     if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
22990c698471Sdan       /* A NOTFOUND or DATA error. Search the table to see if it contains
23000c698471Sdan       ** a row with a matching primary key. If so, this is a DATA conflict.
23010c698471Sdan       ** Otherwise, if there is no primary key match, it is a NOTFOUND. */
23020c698471Sdan 
23030c698471Sdan       rc = sessionConflictHandler(
23040c698471Sdan           SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
23050c698471Sdan       );
23060c698471Sdan 
23070c698471Sdan     }else if( rc==SQLITE_CONSTRAINT ){
2308db04571cSdan       /* This is always a CONSTRAINT conflict. */
2309db04571cSdan       rc = sessionConflictHandler(
2310db04571cSdan           SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
23110c698471Sdan       );
23120c698471Sdan     }
23130c698471Sdan 
23140c698471Sdan   }else{
23150c698471Sdan     assert( op==SQLITE_INSERT );
23167aa469cdSdan     rc = sessionBindRow(pIter, sqlite3changeset_new, nCol, 0, p->pInsert);
23170c698471Sdan     if( rc!=SQLITE_OK ) return rc;
23180c698471Sdan 
23190c698471Sdan     sqlite3_step(p->pInsert);
23200c698471Sdan     rc = sqlite3_reset(p->pInsert);
2321db04571cSdan     if( rc==SQLITE_CONSTRAINT ){
23220c698471Sdan       rc = sessionConflictHandler(
23230c698471Sdan           SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, pbReplace
23240c698471Sdan       );
232537f133ecSdan     }
232637f133ecSdan   }
232737f133ecSdan 
232837f133ecSdan   return rc;
232937f133ecSdan }
233037f133ecSdan 
2331296c7658Sdan /*
2332296c7658Sdan ** Apply the changeset passed via pChangeset/nChangeset to the main database
2333296c7658Sdan ** attached to handle "db". Invoke the supplied conflict handler callback
2334296c7658Sdan ** to resolve any conflicts encountered while applying the change.
2335296c7658Sdan */
2336d5f0767cSdan int sqlite3changeset_apply(
2337296c7658Sdan   sqlite3 *db,                    /* Apply change to "main" db of this handle */
2338296c7658Sdan   int nChangeset,                 /* Size of changeset in bytes */
2339296c7658Sdan   void *pChangeset,               /* Changeset blob */
2340d5f0767cSdan   int(*xConflict)(
2341d5f0767cSdan     void *pCtx,                   /* Copy of fifth arg to _apply() */
2342d5f0767cSdan     int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
2343d5f0767cSdan     sqlite3_changeset_iter *p     /* Handle describing change and conflict */
2344d5f0767cSdan   ),
2345296c7658Sdan   void *pCtx                      /* First argument passed to xConflict */
2346d5f0767cSdan ){
2347296c7658Sdan   sqlite3_changeset_iter *pIter;  /* Iterator to skip through changeset */
2348296c7658Sdan   int rc;                         /* Return code */
2349d5f0767cSdan   const char *zTab = 0;           /* Name of current table */
2350d5f0767cSdan   int nTab = 0;                   /* Result of strlen(zTab) */
2351296c7658Sdan   SessionApplyCtx sApply;         /* changeset_apply() context object */
2352d5f0767cSdan 
23530c698471Sdan   memset(&sApply, 0, sizeof(sApply));
235412ca0b56Sdan   rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
235512ca0b56Sdan   if( rc!=SQLITE_OK ) return rc;
23560c698471Sdan 
23574c220252Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
23580c698471Sdan   rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
23590c698471Sdan   while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter) ){
23600c698471Sdan     int nCol;
2361d5f0767cSdan     int op;
23620c698471Sdan     int bReplace = 0;
23630c698471Sdan     int bRetry = 0;
23640c698471Sdan     const char *zNew;
23650c698471Sdan     sqlite3changeset_op(pIter, &zNew, &nCol, &op);
2366d5f0767cSdan 
23670c698471Sdan     if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){
23680c698471Sdan       sqlite3_free(sApply.azCol);
23690c698471Sdan       sqlite3_finalize(sApply.pDelete);
23700c698471Sdan       sqlite3_finalize(sApply.pUpdate);
23710c698471Sdan       sqlite3_finalize(sApply.pInsert);
23720c698471Sdan       sqlite3_finalize(sApply.pSelect);
23730c698471Sdan       memset(&sApply, 0, sizeof(sApply));
23740c698471Sdan       sApply.db = db;
23750c698471Sdan       sApply.nCol = nCol;
237637f133ecSdan 
2377296c7658Sdan       rc = sessionTableInfo(
2378296c7658Sdan           db, "main", zNew, nCol, &zTab, &sApply.azCol, &sApply.abPK);
23790c698471Sdan 
23800c698471Sdan       if( rc!=SQLITE_OK
23810c698471Sdan        || (rc = sessionSelectRow(db, zTab, &sApply))
23820c698471Sdan        || (rc = sessionUpdateRow(db, zTab, &sApply))
23830c698471Sdan        || (rc = sessionDeleteRow(db, zTab, &sApply))
23840c698471Sdan        || (rc = sessionInsertRow(db, zTab, &sApply))
238537f133ecSdan       ){
238637f133ecSdan         break;
238737f133ecSdan       }
23880c698471Sdan 
23890c698471Sdan       nTab = strlen(zTab);
2390d5f0767cSdan     }
2391d5f0767cSdan 
23920c698471Sdan     rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, &bReplace, &bRetry);
23930c698471Sdan 
23940c698471Sdan     if( rc==SQLITE_OK && bRetry ){
23950c698471Sdan       rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, &bReplace, 0);
23960c698471Sdan     }
23970c698471Sdan 
23980c698471Sdan     if( bReplace ){
2399db04571cSdan       assert( pIter->op==SQLITE_INSERT );
24000c698471Sdan       rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
24010c698471Sdan       if( rc==SQLITE_OK ){
24027aa469cdSdan         rc = sessionBindRow(pIter,
2403db04571cSdan             sqlite3changeset_new, sApply.nCol, sApply.abPK, sApply.pDelete);
24040c698471Sdan         sqlite3_bind_int(sApply.pDelete, sApply.nCol+1, 1);
24050c698471Sdan       }
24060c698471Sdan       if( rc==SQLITE_OK ){
24070c698471Sdan         sqlite3_step(sApply.pDelete);
24080c698471Sdan         rc = sqlite3_reset(sApply.pDelete);
24090c698471Sdan       }
24100c698471Sdan       if( rc==SQLITE_OK ){
24110c698471Sdan         rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, 0, 0);
24120c698471Sdan       }
24130c698471Sdan       if( rc==SQLITE_OK ){
24140c698471Sdan         rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0);
24150c698471Sdan       }
24160c698471Sdan     }
24170c698471Sdan   }
2418d5f0767cSdan 
2419296c7658Sdan   if( rc==SQLITE_OK ){
2420296c7658Sdan     rc = sqlite3changeset_finalize(pIter);
2421296c7658Sdan   }else{
2422296c7658Sdan     sqlite3changeset_finalize(pIter);
2423296c7658Sdan   }
2424d5f0767cSdan 
2425d5f0767cSdan   if( rc==SQLITE_OK ){
2426d5f0767cSdan     rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
2427d5f0767cSdan   }else{
2428d5f0767cSdan     sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
2429d5f0767cSdan     sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
2430d5f0767cSdan   }
2431d5f0767cSdan 
24320c698471Sdan   sqlite3_finalize(sApply.pInsert);
24330c698471Sdan   sqlite3_finalize(sApply.pDelete);
24340c698471Sdan   sqlite3_finalize(sApply.pUpdate);
24350c698471Sdan   sqlite3_finalize(sApply.pSelect);
24360c698471Sdan   sqlite3_free(sApply.azCol);
24374c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
2438d5f0767cSdan   return rc;
2439d5f0767cSdan }
244091ddd559Sdan 
24414fccf43aSdan #endif        /* #ifdef SQLITE_ENABLE_SESSION */
2442