14fccf43aSdan 
29b1c62d4Sdrh #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
34fccf43aSdan 
44fccf43aSdan #include "sqlite3session.h"
54fccf43aSdan #include <assert.h>
64fccf43aSdan #include <string.h>
74fccf43aSdan 
85d8a2984Sdan #ifndef SQLITE_AMALGAMATION
94fccf43aSdan # include "sqliteInt.h"
104fccf43aSdan # include "vdbeInt.h"
115d8a2984Sdan #endif
124fccf43aSdan 
134fccf43aSdan typedef struct SessionTable SessionTable;
144fccf43aSdan typedef struct SessionChange SessionChange;
15296c7658Sdan typedef struct SessionBuffer SessionBuffer;
164fccf43aSdan 
17296c7658Sdan /*
18296c7658Sdan ** Session handle structure.
19296c7658Sdan */
204fccf43aSdan struct sqlite3_session {
214fccf43aSdan   sqlite3 *db;                    /* Database handle session is attached to */
224fccf43aSdan   char *zDb;                      /* Name of database session is attached to */
23296c7658Sdan   int bEnable;                    /* True if currently recording */
24b4480e94Sdan   int bIndirect;                  /* True if all changes are indirect */
25ff4d0f41Sdan   int bAutoAttach;                /* True to auto-attach tables */
264fccf43aSdan   int rc;                         /* Non-zero if an error has occurred */
274fccf43aSdan   sqlite3_session *pNext;         /* Next session object on same db. */
284fccf43aSdan   SessionTable *pTable;           /* List of attached tables */
294fccf43aSdan };
304fccf43aSdan 
314fccf43aSdan /*
32296c7658Sdan ** Structure for changeset iterators.
33296c7658Sdan */
34296c7658Sdan struct sqlite3_changeset_iter {
35296c7658Sdan   u8 *aChangeset;                 /* Pointer to buffer containing changeset */
36296c7658Sdan   int nChangeset;                 /* Number of bytes in aChangeset */
37296c7658Sdan   u8 *pNext;                      /* Pointer to next change within aChangeset */
38296c7658Sdan   int rc;                         /* Iterator error code */
39296c7658Sdan   sqlite3_stmt *pConflict;        /* Points to conflicting row, if any */
40296c7658Sdan   char *zTab;                     /* Current table */
41296c7658Sdan   int nCol;                       /* Number of columns in zTab */
42296c7658Sdan   int op;                         /* Current operation */
43b4480e94Sdan   int bIndirect;                  /* True if current change was indirect */
44244593c8Sdan   u8 *abPK;                       /* Primary key array */
45296c7658Sdan   sqlite3_value **apValue;        /* old.* and new.* values */
46296c7658Sdan };
47296c7658Sdan 
48296c7658Sdan /*
494fccf43aSdan ** Each session object maintains a set of the following structures, one
504fccf43aSdan ** for each table the session object is monitoring. The structures are
514fccf43aSdan ** stored in a linked list starting at sqlite3_session.pTable.
524fccf43aSdan **
534fccf43aSdan ** The keys of the SessionTable.aChange[] hash table are all rows that have
544fccf43aSdan ** been modified in any way since the session object was attached to the
554fccf43aSdan ** table.
564fccf43aSdan **
574fccf43aSdan ** The data associated with each hash-table entry is a structure containing
584fccf43aSdan ** a subset of the initial values that the modified row contained at the
594fccf43aSdan ** start of the session. Or no initial values if the row was inserted.
604fccf43aSdan */
614fccf43aSdan struct SessionTable {
624fccf43aSdan   SessionTable *pNext;
634fccf43aSdan   char *zName;                    /* Local name of table */
644fccf43aSdan   int nCol;                       /* Number of columns in table zName */
65e8d5648eSdan   const char **azCol;             /* Column names */
66e8d5648eSdan   u8 *abPK;                       /* Array of primary key flags */
67296c7658Sdan   int nEntry;                     /* Total number of entries in hash table */
684fccf43aSdan   int nChange;                    /* Size of apChange[] array */
694fccf43aSdan   SessionChange **apChange;       /* Hash table buckets */
704fccf43aSdan };
714fccf43aSdan 
724fccf43aSdan /*
734fccf43aSdan ** RECORD FORMAT:
744fccf43aSdan **
754fccf43aSdan ** The following record format is similar to (but not compatible with) that
764fccf43aSdan ** used in SQLite database files. This format is used as part of the
774fccf43aSdan ** change-set binary format, and so must be architecture independent.
784fccf43aSdan **
794fccf43aSdan ** Unlike the SQLite database record format, each field is self-contained -
804fccf43aSdan ** there is no separation of header and data. Each field begins with a
814fccf43aSdan ** single byte describing its type, as follows:
824fccf43aSdan **
834fccf43aSdan **       0x00: Undefined value.
844fccf43aSdan **       0x01: Integer value.
854fccf43aSdan **       0x02: Real value.
864fccf43aSdan **       0x03: Text value.
874fccf43aSdan **       0x04: Blob value.
884fccf43aSdan **       0x05: SQL NULL value.
894fccf43aSdan **
904fccf43aSdan ** Note that the above match the definitions of SQLITE_INTEGER, SQLITE_TEXT
914fccf43aSdan ** and so on in sqlite3.h. For undefined and NULL values, the field consists
924fccf43aSdan ** only of the single type byte. For other types of values, the type byte
934fccf43aSdan ** is followed by:
944fccf43aSdan **
954fccf43aSdan **   Text values:
964fccf43aSdan **     A varint containing the number of bytes in the value (encoded using
974fccf43aSdan **     UTF-8). Followed by a buffer containing the UTF-8 representation
984fccf43aSdan **     of the text value. There is no nul terminator.
994fccf43aSdan **
1004fccf43aSdan **   Blob values:
1014fccf43aSdan **     A varint containing the number of bytes in the value, followed by
1024fccf43aSdan **     a buffer containing the value itself.
1034fccf43aSdan **
1044fccf43aSdan **   Integer values:
1054fccf43aSdan **     An 8-byte big-endian integer value.
1064fccf43aSdan **
1074fccf43aSdan **   Real values:
1084fccf43aSdan **     An 8-byte big-endian IEEE 754-2008 real value.
1094fccf43aSdan **
1104fccf43aSdan ** Varint values are encoded in the same way as varints in the SQLite
1114fccf43aSdan ** record format.
1124fccf43aSdan **
1134fccf43aSdan ** CHANGESET FORMAT:
1144fccf43aSdan **
1154fccf43aSdan ** A changeset is a collection of DELETE, UPDATE and INSERT operations on
1164fccf43aSdan ** one or more tables. Operations on a single table are grouped together,
1174fccf43aSdan ** but may occur in any order (i.e. deletes, updates and inserts are all
1184fccf43aSdan ** mixed together).
1194fccf43aSdan **
1204fccf43aSdan ** Each group of changes begins with a table header:
1214fccf43aSdan **
1224fccf43aSdan **   1 byte: Constant 0x54 (capital 'T')
1234fccf43aSdan **   Varint: Big-endian integer set to the number of columns in the table.
1244fccf43aSdan **   N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated.
1254fccf43aSdan **
1264fccf43aSdan ** Followed by one or more changes to the table.
1274fccf43aSdan **
1284fccf43aSdan **   1 byte: Either SQLITE_INSERT, UPDATE or DELETE.
1295d607a6eSdan **   1 byte: The "indirect-change" flag.
1304fccf43aSdan **   old.* record: (delete and update only)
1314fccf43aSdan **   new.* record: (insert and update only)
1324fccf43aSdan */
1334fccf43aSdan 
1344fccf43aSdan /*
1354fccf43aSdan ** For each row modified during a session, there exists a single instance of
1364fccf43aSdan ** this structure stored in a SessionTable.aChange[] hash table.
1374fccf43aSdan */
1384fccf43aSdan struct SessionChange {
139e8d5648eSdan   int bInsert;                    /* True if row was inserted this session */
140b4480e94Sdan   int bIndirect;                  /* True if this change is "indirect" */
1414fccf43aSdan   int nRecord;                    /* Number of bytes in buffer aRecord[] */
1424fccf43aSdan   u8 *aRecord;                    /* Buffer containing old.* record */
1434fccf43aSdan   SessionChange *pNext;           /* For hash-table collisions */
1444fccf43aSdan };
1454fccf43aSdan 
146296c7658Sdan /*
147296c7658Sdan ** Instances of this structure are used to build strings or binary records.
148296c7658Sdan */
149296c7658Sdan struct SessionBuffer {
150296c7658Sdan   u8 *aBuf;                       /* Pointer to changeset buffer */
151296c7658Sdan   int nBuf;                       /* Size of buffer aBuf */
152296c7658Sdan   int nAlloc;                     /* Size of allocation containing aBuf */
153296c7658Sdan };
1544fccf43aSdan 
155296c7658Sdan /*
156296c7658Sdan ** Write a varint with value iVal into the buffer at aBuf. Return the
157296c7658Sdan ** number of bytes written.
158296c7658Sdan */
159296c7658Sdan static int sessionVarintPut(u8 *aBuf, int iVal){
160296c7658Sdan   return putVarint32(aBuf, iVal);
1614fccf43aSdan }
1624fccf43aSdan 
163296c7658Sdan /*
164296c7658Sdan ** Return the number of bytes required to store value iVal as a varint.
165296c7658Sdan */
166296c7658Sdan static int sessionVarintLen(int iVal){
167296c7658Sdan   return sqlite3VarintLen(iVal);
168296c7658Sdan }
169296c7658Sdan 
170296c7658Sdan /*
171296c7658Sdan ** Read a varint value from aBuf[] into *piVal. Return the number of
172296c7658Sdan ** bytes read.
173296c7658Sdan */
1744fccf43aSdan static int sessionVarintGet(u8 *aBuf, int *piVal){
175296c7658Sdan   return getVarint32(aBuf, *piVal);
1764fccf43aSdan }
1774fccf43aSdan 
178296c7658Sdan /*
179296c7658Sdan ** Read a 64-bit big-endian integer value from buffer aRec[]. Return
180296c7658Sdan ** the value read.
181296c7658Sdan */
1824fccf43aSdan static sqlite3_int64 sessionGetI64(u8 *aRec){
1834fccf43aSdan   return (((sqlite3_int64)aRec[0]) << 56)
1844fccf43aSdan        + (((sqlite3_int64)aRec[1]) << 48)
1854fccf43aSdan        + (((sqlite3_int64)aRec[2]) << 40)
1864fccf43aSdan        + (((sqlite3_int64)aRec[3]) << 32)
1874fccf43aSdan        + (((sqlite3_int64)aRec[4]) << 24)
1884fccf43aSdan        + (((sqlite3_int64)aRec[5]) << 16)
1894fccf43aSdan        + (((sqlite3_int64)aRec[6]) <<  8)
1904fccf43aSdan        + (((sqlite3_int64)aRec[7]) <<  0);
1914fccf43aSdan }
1924fccf43aSdan 
1934fccf43aSdan /*
194296c7658Sdan ** Write a 64-bit big-endian integer value to the buffer aBuf[].
195296c7658Sdan */
196296c7658Sdan static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){
197296c7658Sdan   aBuf[0] = (i>>56) & 0xFF;
198296c7658Sdan   aBuf[1] = (i>>48) & 0xFF;
199296c7658Sdan   aBuf[2] = (i>>40) & 0xFF;
200296c7658Sdan   aBuf[3] = (i>>32) & 0xFF;
201296c7658Sdan   aBuf[4] = (i>>24) & 0xFF;
202296c7658Sdan   aBuf[5] = (i>>16) & 0xFF;
203296c7658Sdan   aBuf[6] = (i>> 8) & 0xFF;
204296c7658Sdan   aBuf[7] = (i>> 0) & 0xFF;
205296c7658Sdan }
206296c7658Sdan 
207296c7658Sdan /*
2084fccf43aSdan ** This function is used to serialize the contents of value pValue (see
2094fccf43aSdan ** comment titled "RECORD FORMAT" above).
2104fccf43aSdan **
2114fccf43aSdan ** If it is non-NULL, the serialized form of the value is written to
2124fccf43aSdan ** buffer aBuf. *pnWrite is set to the number of bytes written before
2134fccf43aSdan ** returning. Or, if aBuf is NULL, the only thing this function does is
2144fccf43aSdan ** set *pnWrite.
2154fccf43aSdan **
2164fccf43aSdan ** If no error occurs, SQLITE_OK is returned. Or, if an OOM error occurs
2174fccf43aSdan ** within a call to sqlite3_value_text() (may fail if the db is utf-16))
2184fccf43aSdan ** SQLITE_NOMEM is returned.
2194fccf43aSdan */
2204fccf43aSdan static int sessionSerializeValue(
2214fccf43aSdan   u8 *aBuf,                       /* If non-NULL, write serialized value here */
2224fccf43aSdan   sqlite3_value *pValue,          /* Value to serialize */
2234fccf43aSdan   int *pnWrite                    /* IN/OUT: Increment by bytes written */
2244fccf43aSdan ){
225296c7658Sdan   int nByte;                      /* Size of serialized value in bytes */
2264fccf43aSdan 
227*80fe2d93Sdan   if( pValue ){
228*80fe2d93Sdan     int eType;                    /* Value type (SQLITE_NULL, TEXT etc.) */
229*80fe2d93Sdan 
2304fccf43aSdan     eType = sqlite3_value_type(pValue);
2314fccf43aSdan     if( aBuf ) aBuf[0] = eType;
2324fccf43aSdan 
2334fccf43aSdan     switch( eType ){
2344fccf43aSdan       case SQLITE_NULL:
2354fccf43aSdan         nByte = 1;
2364fccf43aSdan         break;
2374fccf43aSdan 
2384fccf43aSdan       case SQLITE_INTEGER:
2394fccf43aSdan       case SQLITE_FLOAT:
2404fccf43aSdan         if( aBuf ){
2414fccf43aSdan           /* TODO: SQLite does something special to deal with mixed-endian
2424fccf43aSdan           ** floating point values (e.g. ARM7). This code probably should
2434fccf43aSdan           ** too.  */
2444fccf43aSdan           u64 i;
2454fccf43aSdan           if( eType==SQLITE_INTEGER ){
2464fccf43aSdan             i = (u64)sqlite3_value_int64(pValue);
2474fccf43aSdan           }else{
2484fccf43aSdan             double r;
2494fccf43aSdan             assert( sizeof(double)==8 && sizeof(u64)==8 );
2504fccf43aSdan             r = sqlite3_value_double(pValue);
2514fccf43aSdan             memcpy(&i, &r, 8);
2524fccf43aSdan           }
253296c7658Sdan           sessionPutI64(&aBuf[1], i);
2544fccf43aSdan         }
2554fccf43aSdan         nByte = 9;
2564fccf43aSdan         break;
2574fccf43aSdan 
2584e895da1Sdan       default: {
259*80fe2d93Sdan         u8 *z;
260*80fe2d93Sdan         int n;
261*80fe2d93Sdan         int nVarint;
262*80fe2d93Sdan 
2634e895da1Sdan         assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
264*80fe2d93Sdan         if( eType==SQLITE_TEXT ){
265*80fe2d93Sdan           z = (u8 *)sqlite3_value_text(pValue);
266*80fe2d93Sdan         }else{
267*80fe2d93Sdan           z = (u8 *)sqlite3_value_blob(pValue);
268*80fe2d93Sdan         }
269*80fe2d93Sdan         if( z==0 ) return SQLITE_NOMEM;
270*80fe2d93Sdan         n = sqlite3_value_bytes(pValue);
271*80fe2d93Sdan         nVarint = sessionVarintLen(n);
272*80fe2d93Sdan 
2734fccf43aSdan         if( aBuf ){
2744fccf43aSdan           sessionVarintPut(&aBuf[1], n);
2754fccf43aSdan           memcpy(&aBuf[nVarint + 1], eType==SQLITE_TEXT ?
2764fccf43aSdan               sqlite3_value_text(pValue) : sqlite3_value_blob(pValue), n
2774fccf43aSdan           );
2784fccf43aSdan         }
2794fccf43aSdan 
2804fccf43aSdan         nByte = 1 + nVarint + n;
2814fccf43aSdan         break;
2824fccf43aSdan       }
2834fccf43aSdan     }
284*80fe2d93Sdan   }else{
285*80fe2d93Sdan     nByte = 1;
286*80fe2d93Sdan     if( aBuf ) aBuf[0] = '\0';
287*80fe2d93Sdan   }
2884fccf43aSdan 
2894fccf43aSdan   *pnWrite += nByte;
2904fccf43aSdan   return SQLITE_OK;
2914fccf43aSdan }
2924fccf43aSdan 
2934131639cSdan #define HASH_APPEND(hash, add) ((hash) << 3) ^ (hash) ^ (unsigned int)(add)
2944131639cSdan static unsigned int sessionHashAppendI64(unsigned int h, i64 i){
295e8d5648eSdan   h = HASH_APPEND(h, i & 0xFFFFFFFF);
296e8d5648eSdan   return HASH_APPEND(h, (i>>32)&0xFFFFFFFF);
297e8d5648eSdan }
2984131639cSdan static unsigned int sessionHashAppendBlob(unsigned int h, int n, const u8 *z){
299e8d5648eSdan   int i;
300e8d5648eSdan   for(i=0; i<n; i++) h = HASH_APPEND(h, z[i]);
301e8d5648eSdan   return h;
302e8d5648eSdan }
303e8d5648eSdan 
3044fccf43aSdan /*
3054131639cSdan ** This function may only be called from within a pre-update callback.
3064131639cSdan ** It calculates a hash based on the primary key values of the old.* or
3074131639cSdan ** new.* row currently available. The value returned is guaranteed to
3084131639cSdan ** be less than pTab->nBucket.
3094fccf43aSdan */
3104131639cSdan static unsigned int sessionPreupdateHash(
311e8d5648eSdan   sqlite3 *db,                    /* Database handle */
312e8d5648eSdan   SessionTable *pTab,             /* Session table handle */
313e8d5648eSdan   int bNew,                       /* True to hash the new.* PK */
31427453faeSdan   int *piHash,                    /* OUT: Hash value */
31527453faeSdan   int *pbNullPK
316e8d5648eSdan ){
3174131639cSdan   unsigned int h = 0;             /* Hash value to return */
3184131639cSdan   int i;                          /* Used to iterate through columns */
319e8d5648eSdan 
32027453faeSdan   assert( *pbNullPK==0 );
321e8d5648eSdan   assert( pTab->nCol==sqlite3_preupdate_count(db) );
322e8d5648eSdan   for(i=0; i<pTab->nCol; i++){
323e8d5648eSdan     if( pTab->abPK[i] ){
324e8d5648eSdan       int rc;
325e8d5648eSdan       int eType;
326e8d5648eSdan       sqlite3_value *pVal;
327e8d5648eSdan 
328e8d5648eSdan       if( bNew ){
329e8d5648eSdan         rc = sqlite3_preupdate_new(db, i, &pVal);
330e8d5648eSdan       }else{
331e8d5648eSdan         rc = sqlite3_preupdate_old(db, i, &pVal);
332e8d5648eSdan       }
33312ca0b56Sdan       if( rc!=SQLITE_OK ) return rc;
334e8d5648eSdan 
335e8d5648eSdan       eType = sqlite3_value_type(pVal);
336e8d5648eSdan       h = HASH_APPEND(h, eType);
3376734007dSdan       if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
338e8d5648eSdan         i64 iVal;
339e8d5648eSdan         if( eType==SQLITE_INTEGER ){
340e8d5648eSdan           iVal = sqlite3_value_int64(pVal);
341e8d5648eSdan         }else{
342e8d5648eSdan           double rVal = sqlite3_value_double(pVal);
343e8d5648eSdan           assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
344e8d5648eSdan           memcpy(&iVal, &rVal, 8);
345e8d5648eSdan         }
346e8d5648eSdan         h = sessionHashAppendI64(h, iVal);
3476734007dSdan       }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
3486734007dSdan         const u8 *z;
3496734007dSdan         if( eType==SQLITE_TEXT ){
3506734007dSdan           z = (const u8 *)sqlite3_value_text(pVal);
3516734007dSdan         }else{
3526734007dSdan           z = (const u8 *)sqlite3_value_blob(pVal);
353e8d5648eSdan         }
3546734007dSdan         if( !z ) return SQLITE_NOMEM;
3556734007dSdan         h = sessionHashAppendBlob(h, sqlite3_value_bytes(pVal), z);
3566734007dSdan       }else{
35727453faeSdan         assert( eType==SQLITE_NULL );
35827453faeSdan         *pbNullPK = 1;
35927453faeSdan         return SQLITE_OK;
360e8d5648eSdan       }
361e8d5648eSdan     }
362e8d5648eSdan   }
363e8d5648eSdan 
364e8d5648eSdan   *piHash = (h % pTab->nChange);
365e8d5648eSdan   return SQLITE_OK;
366e8d5648eSdan }
367e8d5648eSdan 
3684131639cSdan /*
3696cda207fSdan ** The buffer that the argument points to contains a serialized SQL value.
3706cda207fSdan ** Return the number of bytes of space occupied by the value (including
3716cda207fSdan ** the type byte).
3726cda207fSdan */
3736cda207fSdan static int sessionSerialLen(u8 *a){
3746cda207fSdan   int e = *a;
3756cda207fSdan   int n;
3766cda207fSdan   if( e==0 ) return 1;
3776cda207fSdan   if( e==SQLITE_NULL ) return 1;
3786cda207fSdan   if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
3796cda207fSdan   return sessionVarintGet(&a[1], &n) + 1 + n;
3806cda207fSdan }
3816cda207fSdan 
3826cda207fSdan /*
3835d607a6eSdan ** Based on the primary key values stored in change aRecord, calculate a
3844131639cSdan ** hash key, assuming the has table has nBucket buckets. The hash keys
3854131639cSdan ** calculated by this function are compatible with those calculated by
3864131639cSdan ** sessionPreupdateHash().
3874131639cSdan */
3884131639cSdan static unsigned int sessionChangeHash(
3894131639cSdan   SessionTable *pTab,             /* Table handle */
3905d607a6eSdan   u8 *aRecord,                    /* Change record */
3914131639cSdan   int nBucket                     /* Assume this many buckets in hash table */
392e8d5648eSdan ){
3934131639cSdan   unsigned int h = 0;             /* Value to return */
3944131639cSdan   int i;                          /* Used to iterate through columns */
3955d607a6eSdan   u8 *a = aRecord;                /* Used to iterate through change record */
396e8d5648eSdan 
397e8d5648eSdan   for(i=0; i<pTab->nCol; i++){
3986cda207fSdan     int eType = *a;
399e8d5648eSdan     int isPK = pTab->abPK[i];
400e8d5648eSdan 
40127453faeSdan     /* It is not possible for eType to be SQLITE_NULL here. The session
40227453faeSdan     ** module does not record changes for rows with NULL values stored in
40327453faeSdan     ** primary key columns. */
40427453faeSdan     assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
40527453faeSdan          || eType==SQLITE_TEXT || eType==SQLITE_BLOB
4066cda207fSdan          || eType==SQLITE_NULL || eType==0
40727453faeSdan     );
4086cda207fSdan     assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) );
40927453faeSdan 
4106cda207fSdan     if( isPK ){
4116cda207fSdan       a++;
4126cda207fSdan       h = HASH_APPEND(h, eType);
41327453faeSdan       if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
4146734007dSdan         h = sessionHashAppendI64(h, sessionGetI64(a));
415e8d5648eSdan         a += 8;
41627453faeSdan       }else{
417e8d5648eSdan         int n;
418e8d5648eSdan         a += sessionVarintGet(a, &n);
4196734007dSdan         h = sessionHashAppendBlob(h, n, a);
420e8d5648eSdan         a += n;
421e8d5648eSdan       }
4226cda207fSdan     }else{
4236cda207fSdan       a += sessionSerialLen(a);
4246cda207fSdan     }
425e8d5648eSdan   }
426e8d5648eSdan   return (h % nBucket);
427e8d5648eSdan }
428e8d5648eSdan 
4295d607a6eSdan static int sessionChangeEqual(
4305d607a6eSdan   SessionTable *pTab,
4315d607a6eSdan   u8 *aLeft,                      /* Change record */
4325d607a6eSdan   u8 *aRight                      /* Change record */
4335d607a6eSdan ){
4345d607a6eSdan   u8 *a1 = aLeft;
4355d607a6eSdan   u8 *a2 = aRight;
4365d607a6eSdan   int i;
4375d607a6eSdan 
4385d607a6eSdan   for(i=0; i<pTab->nCol; i++){
4395d607a6eSdan     int n1 = sessionSerialLen(a1);
4405d607a6eSdan     int n2 = sessionSerialLen(a2);
4415d607a6eSdan 
4425d607a6eSdan     if( pTab->abPK[i] && (n1!=n2 || memcmp(a1, a2, n1)) ){
4435d607a6eSdan       return 0;
4445d607a6eSdan     }
4455d607a6eSdan     a1 += n1;
4466cda207fSdan     a2 += n2;
4475d607a6eSdan   }
4485d607a6eSdan 
4495d607a6eSdan   return 1;
4505d607a6eSdan }
4515d607a6eSdan 
4525d607a6eSdan static void sessionMergeRecord(
4535d607a6eSdan   u8 **paOut,
4545d607a6eSdan   SessionTable *pTab,
4555d607a6eSdan   u8 *aLeft,
4565d607a6eSdan   u8 *aRight
4575d607a6eSdan ){
4585d607a6eSdan   u8 *a1 = aLeft;
4595d607a6eSdan   u8 *a2 = aRight;
4605d607a6eSdan   u8 *aOut = *paOut;
4615d607a6eSdan   int i;
4625d607a6eSdan 
4635d607a6eSdan   for(i=0; i<pTab->nCol; i++){
4645d607a6eSdan     int n1 = sessionSerialLen(a1);
4655d607a6eSdan     int n2 = sessionSerialLen(a2);
4665d607a6eSdan     if( *a2 ){
4675d607a6eSdan       memcpy(aOut, a2, n2);
4685d607a6eSdan       aOut += n2;
4695d607a6eSdan     }else{
4705d607a6eSdan       memcpy(aOut, a1, n1);
4715d607a6eSdan       aOut += n1;
4725d607a6eSdan     }
4735d607a6eSdan     a1 += n1;
4745d607a6eSdan     a2 += n2;
4755d607a6eSdan   }
4765d607a6eSdan 
4775d607a6eSdan   *paOut = aOut;
4785d607a6eSdan }
4795d607a6eSdan 
4805d607a6eSdan static u8 *sessionMergeValue(
4815d607a6eSdan   u8 **paOne,
4825d607a6eSdan   u8 **paTwo,
4835d607a6eSdan   int *pnVal
4845d607a6eSdan ){
4855d607a6eSdan   u8 *a1 = *paOne;
4865d607a6eSdan   u8 *a2 = *paTwo;
4875d607a6eSdan   u8 *pRet = 0;
4885d607a6eSdan   int n1;
4895d607a6eSdan 
4905d607a6eSdan   assert( a1 );
4915d607a6eSdan   if( a2 ){
4925d607a6eSdan     int n2 = sessionSerialLen(a2);
4935d607a6eSdan     if( *a2 ){
4945d607a6eSdan       *pnVal = n2;
4955d607a6eSdan       pRet = a2;
4965d607a6eSdan     }
4975d607a6eSdan     *paTwo = &a2[n2];
4985d607a6eSdan   }
4995d607a6eSdan 
5005d607a6eSdan   n1 = sessionSerialLen(a1);
5015d607a6eSdan   if( pRet==0 ){
5025d607a6eSdan     *pnVal = n1;
5035d607a6eSdan     pRet = a1;
5045d607a6eSdan   }
5055d607a6eSdan   *paOne = &a1[n1];
5065d607a6eSdan 
5075d607a6eSdan   return pRet;
5085d607a6eSdan }
5095d607a6eSdan 
5105d607a6eSdan static int sessionMergeUpdate(
5115d607a6eSdan   u8 **paOut,
5125d607a6eSdan   SessionTable *pTab,
5135d607a6eSdan   u8 *aOldRecord1,
5145d607a6eSdan   u8 *aOldRecord2,
5155d607a6eSdan   u8 *aNewRecord1,
5165d607a6eSdan   u8 *aNewRecord2
5175d607a6eSdan ){
5185d607a6eSdan   u8 *aOld1 = aOldRecord1;
5195d607a6eSdan   u8 *aOld2 = aOldRecord2;
5205d607a6eSdan   u8 *aNew1 = aNewRecord1;
5215d607a6eSdan   u8 *aNew2 = aNewRecord2;
5225d607a6eSdan 
5235d607a6eSdan   u8 *aOut = *paOut;
5245d607a6eSdan   int i;
5255d607a6eSdan   int bRequired = 0;
5265d607a6eSdan 
5275d607a6eSdan   assert( aOldRecord1 && aNewRecord1 );
5285d607a6eSdan 
5295d607a6eSdan   /* Write the old.* vector first. */
5305d607a6eSdan   for(i=0; i<pTab->nCol; i++){
5315d607a6eSdan     int nOld;
5325d607a6eSdan     u8 *aOld;
5335d607a6eSdan     int nNew;
5345d607a6eSdan     u8 *aNew;
5355d607a6eSdan 
5365d607a6eSdan     aOld = sessionMergeValue(&aOld1, &aOld2, &nOld);
5375d607a6eSdan     aNew = sessionMergeValue(&aNew1, &aNew2, &nNew);
5385d607a6eSdan     if( pTab->abPK[i] || nOld!=nNew || memcmp(aOld, aNew, nNew) ){
5395d607a6eSdan       if( pTab->abPK[i]==0 ) bRequired = 1;
5405d607a6eSdan       memcpy(aOut, aOld, nOld);
5415d607a6eSdan       aOut += nOld;
5425d607a6eSdan     }else{
5435d607a6eSdan       *(aOut++) = '\0';
5445d607a6eSdan     }
5455d607a6eSdan   }
5465d607a6eSdan 
5475d607a6eSdan   if( !bRequired ) return 0;
5485d607a6eSdan 
5495d607a6eSdan   /* Write the new.* vector */
5505d607a6eSdan   aOld1 = aOldRecord1;
5515d607a6eSdan   aOld2 = aOldRecord2;
5525d607a6eSdan   aNew1 = aNewRecord1;
5535d607a6eSdan   aNew2 = aNewRecord2;
5545d607a6eSdan   for(i=0; i<pTab->nCol; i++){
5555d607a6eSdan     int nOld;
5565d607a6eSdan     u8 *aOld;
5575d607a6eSdan     int nNew;
5585d607a6eSdan     u8 *aNew;
5595d607a6eSdan 
5605d607a6eSdan     aOld = sessionMergeValue(&aOld1, &aOld2, &nOld);
5615d607a6eSdan     aNew = sessionMergeValue(&aNew1, &aNew2, &nNew);
5625d607a6eSdan     if( pTab->abPK[i] || (nOld==nNew && 0==memcmp(aOld, aNew, nNew)) ){
5635d607a6eSdan       *(aOut++) = '\0';
5645d607a6eSdan     }else{
5655d607a6eSdan       memcpy(aOut, aNew, nNew);
5665d607a6eSdan       aOut += nNew;
5675d607a6eSdan     }
5685d607a6eSdan   }
5695d607a6eSdan 
5705d607a6eSdan   *paOut = aOut;
5715d607a6eSdan   return 1;
5725d607a6eSdan }
5735d607a6eSdan 
5746734007dSdan static void sessionPreupdateEqual(
575e8d5648eSdan   sqlite3 *db,
576e8d5648eSdan   SessionTable *pTab,
577e8d5648eSdan   SessionChange *pChange,
578e8d5648eSdan   int bNew,
579e8d5648eSdan   int *pbEqual
580e8d5648eSdan ){
581e8d5648eSdan   int i;
582e8d5648eSdan   u8 *a = pChange->aRecord;
583e8d5648eSdan 
584e8d5648eSdan   *pbEqual = 0;
585e8d5648eSdan 
586e8d5648eSdan   for(i=0; i<pTab->nCol; i++){
587e8d5648eSdan     int eType = *a++;
588e8d5648eSdan     if( !pTab->abPK[i] ){
589e8d5648eSdan       switch( eType ){
590e8d5648eSdan         case SQLITE_INTEGER:
591e8d5648eSdan         case SQLITE_FLOAT:
592e8d5648eSdan           a += 8;
593e8d5648eSdan           break;
594e8d5648eSdan 
595e8d5648eSdan         case SQLITE_TEXT:
596e8d5648eSdan         case SQLITE_BLOB: {
597e8d5648eSdan           int n;
598e8d5648eSdan           a += sessionVarintGet(a, &n);
599e8d5648eSdan           a += n;
600e8d5648eSdan           break;
601e8d5648eSdan         }
602e8d5648eSdan       }
603e8d5648eSdan     }else{
6046734007dSdan       sqlite3_value *pVal;        /* Value returned by preupdate_new/old */
6056734007dSdan       int rc;                     /* Error code from preupdate_new/old */
6066734007dSdan 
6076734007dSdan       /* The following calls to preupdate_new() and preupdate_old() can not
6086734007dSdan       ** fail. This is because they cache their return values, and by the
6096734007dSdan       ** time control flows to here they have already been called once from
6106734007dSdan       ** within sessionPreupdateHash(). The first two asserts below verify
6116734007dSdan       ** this (that the method has already been called). */
612e8d5648eSdan       if( bNew ){
6136734007dSdan         assert( db->pPreUpdate->pNewUnpacked || db->pPreUpdate->aNew );
614e8d5648eSdan         rc = sqlite3_preupdate_new(db, i, &pVal);
615e8d5648eSdan       }else{
6166734007dSdan         assert( db->pPreUpdate->pUnpacked );
617e8d5648eSdan         rc = sqlite3_preupdate_old(db, i, &pVal);
618e8d5648eSdan       }
6196734007dSdan       assert( rc==SQLITE_OK );
6206734007dSdan       if( sqlite3_value_type(pVal)!=eType ) return;
621e8d5648eSdan 
62212ca0b56Sdan       /* A SessionChange object never has a NULL value in a PK column */
62312ca0b56Sdan       assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
62412ca0b56Sdan            || eType==SQLITE_BLOB    || eType==SQLITE_TEXT
62512ca0b56Sdan       );
62612ca0b56Sdan 
62712ca0b56Sdan       if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
628e8d5648eSdan         i64 iVal = sessionGetI64(a);
629e8d5648eSdan         a += 8;
630e8d5648eSdan         if( eType==SQLITE_INTEGER ){
6316734007dSdan           if( sqlite3_value_int64(pVal)!=iVal ) return;
632e8d5648eSdan         }else{
633e8d5648eSdan           double rVal;
634e8d5648eSdan           assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
635e8d5648eSdan           memcpy(&rVal, &iVal, 8);
6366734007dSdan           if( sqlite3_value_double(pVal)!=rVal ) return;
637e8d5648eSdan         }
63812ca0b56Sdan       }else{
639e8d5648eSdan         int n;
640e8d5648eSdan         const u8 *z;
641e8d5648eSdan         a += sessionVarintGet(a, &n);
6426734007dSdan         if( sqlite3_value_bytes(pVal)!=n ) return;
64312ca0b56Sdan         if( eType==SQLITE_TEXT ){
64412ca0b56Sdan           z = sqlite3_value_text(pVal);
64512ca0b56Sdan         }else{
64612ca0b56Sdan           z = sqlite3_value_blob(pVal);
64712ca0b56Sdan         }
6486734007dSdan         if( memcmp(a, z, n) ) return;
649e8d5648eSdan         a += n;
650e8d5648eSdan         break;
651e8d5648eSdan       }
652e8d5648eSdan     }
653e8d5648eSdan   }
654e8d5648eSdan 
655e8d5648eSdan   *pbEqual = 1;
6566734007dSdan   return;
6574fccf43aSdan }
6584fccf43aSdan 
6594fccf43aSdan /*
6604fccf43aSdan ** If required, grow the hash table used to store changes on table pTab
6614fccf43aSdan ** (part of the session pSession). If a fatal OOM error occurs, set the
6624fccf43aSdan ** session object to failed and return SQLITE_ERROR. Otherwise, return
6634fccf43aSdan ** SQLITE_OK.
6644fccf43aSdan **
6654fccf43aSdan ** It is possible that a non-fatal OOM error occurs in this function. In
6664fccf43aSdan ** that case the hash-table does not grow, but SQLITE_OK is returned anyway.
6674fccf43aSdan ** Growing the hash table in this case is a performance optimization only,
6684fccf43aSdan ** it is not required for correct operation.
6694fccf43aSdan */
6705d607a6eSdan static int sessionGrowHash(SessionTable *pTab){
6714fccf43aSdan   if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
6724fccf43aSdan     int i;
6734fccf43aSdan     SessionChange **apNew;
6744fccf43aSdan     int nNew = (pTab->nChange ? pTab->nChange : 128) * 2;
6754fccf43aSdan 
6764fccf43aSdan     apNew = (SessionChange **)sqlite3_malloc(sizeof(SessionChange *) * nNew);
6774fccf43aSdan     if( apNew==0 ){
6784fccf43aSdan       if( pTab->nChange==0 ){
6794fccf43aSdan         return SQLITE_ERROR;
6804fccf43aSdan       }
6814fccf43aSdan       return SQLITE_OK;
6824fccf43aSdan     }
6834fccf43aSdan     memset(apNew, 0, sizeof(SessionChange *) * nNew);
6844fccf43aSdan 
6854fccf43aSdan     for(i=0; i<pTab->nChange; i++){
6864fccf43aSdan       SessionChange *p;
6874fccf43aSdan       SessionChange *pNext;
6884fccf43aSdan       for(p=pTab->apChange[i]; p; p=pNext){
6895d607a6eSdan         int iHash = sessionChangeHash(pTab, p->aRecord, nNew);
6904fccf43aSdan         pNext = p->pNext;
6914fccf43aSdan         p->pNext = apNew[iHash];
6924fccf43aSdan         apNew[iHash] = p;
6934fccf43aSdan       }
6944fccf43aSdan     }
6954fccf43aSdan 
6964fccf43aSdan     sqlite3_free(pTab->apChange);
6974fccf43aSdan     pTab->nChange = nNew;
6984fccf43aSdan     pTab->apChange = apNew;
6994fccf43aSdan   }
7004fccf43aSdan 
7014fccf43aSdan   return SQLITE_OK;
7024fccf43aSdan }
7034fccf43aSdan 
704296c7658Sdan /*
705e8d5648eSdan ** This function queries the database for the names of the columns of table
706e8d5648eSdan ** zThis, in schema zDb. It is expected that the table has nCol columns. If
707e8d5648eSdan ** not, SQLITE_SCHEMA is returned and none of the output variables are
708e8d5648eSdan ** populated.
709e8d5648eSdan **
710e8d5648eSdan ** Otherwise, if it is not NULL, variable *pzTab is set to point to a
711e8d5648eSdan ** nul-terminated copy of the table name. *pazCol (if not NULL) is set to
712e8d5648eSdan ** point to an array of pointers to column names. And *pabPK (again, if not
713e8d5648eSdan ** NULL) is set to point to an array of booleans - true if the corresponding
714e8d5648eSdan ** column is part of the primary key.
715e8d5648eSdan **
716e8d5648eSdan ** For example, if the table is declared as:
717e8d5648eSdan **
718e8d5648eSdan **     CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z));
719e8d5648eSdan **
720e8d5648eSdan ** Then the three output variables are populated as follows:
721e8d5648eSdan **
722e8d5648eSdan **     *pzTab  = "tbl1"
723e8d5648eSdan **     *pazCol = {"w", "x", "y", "z"}
724e8d5648eSdan **     *pabPK  = {1, 0, 0, 1}
725e8d5648eSdan **
726e8d5648eSdan ** All returned buffers are part of the same single allocation, which must
727e8d5648eSdan ** be freed using sqlite3_free() by the caller. If pazCol was not NULL, then
728e8d5648eSdan ** pointer *pazCol should be freed to release all memory. Otherwise, pointer
729e8d5648eSdan ** *pabPK. It is illegal for both pazCol and pabPK to be NULL.
730e8d5648eSdan */
731e8d5648eSdan static int sessionTableInfo(
732e8d5648eSdan   sqlite3 *db,                    /* Database connection */
733e8d5648eSdan   const char *zDb,                /* Name of attached database (e.g. "main") */
734e8d5648eSdan   const char *zThis,              /* Table name */
735ca62ad57Sdan   int *pnCol,                     /* OUT: number of columns */
736e8d5648eSdan   const char **pzTab,             /* OUT: Copy of zThis */
737e8d5648eSdan   const char ***pazCol,           /* OUT: Array of column names for table */
738e8d5648eSdan   u8 **pabPK                      /* OUT: Array of booleans - true for PK col */
739e8d5648eSdan ){
740e8d5648eSdan   char *zPragma;
741e8d5648eSdan   sqlite3_stmt *pStmt;
742e8d5648eSdan   int rc;
743e8d5648eSdan   int nByte;
744e8d5648eSdan   int nDbCol = 0;
745e8d5648eSdan   int nThis;
746e8d5648eSdan   int i;
747e8d5648eSdan   u8 *pAlloc;
748db04571cSdan   char **azCol = 0;
749e8d5648eSdan   u8 *abPK;
750e8d5648eSdan 
751db04571cSdan   assert( pazCol && pabPK );
752e8d5648eSdan 
753cfdbde21Sdrh   nThis = sqlite3Strlen30(zThis);
754e8d5648eSdan   zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
755e8d5648eSdan   if( !zPragma ) return SQLITE_NOMEM;
756e8d5648eSdan 
757e8d5648eSdan   rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
758e8d5648eSdan   sqlite3_free(zPragma);
759e8d5648eSdan   if( rc!=SQLITE_OK ) return rc;
760e8d5648eSdan 
761e8d5648eSdan   nByte = nThis + 1;
762e8d5648eSdan   while( SQLITE_ROW==sqlite3_step(pStmt) ){
763e8d5648eSdan     nByte += sqlite3_column_bytes(pStmt, 1);
764e8d5648eSdan     nDbCol++;
765e8d5648eSdan   }
766e8d5648eSdan   rc = sqlite3_reset(pStmt);
767e8d5648eSdan 
768e8d5648eSdan   if( rc==SQLITE_OK ){
769e8d5648eSdan     nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
770e8d5648eSdan     pAlloc = sqlite3_malloc(nByte);
771e8d5648eSdan     if( pAlloc==0 ){
772e8d5648eSdan       rc = SQLITE_NOMEM;
773e8d5648eSdan     }
774e8d5648eSdan   }
775e8d5648eSdan   if( rc==SQLITE_OK ){
776e8d5648eSdan     azCol = (char **)pAlloc;
777ca62ad57Sdan     pAlloc = (u8 *)&azCol[nDbCol];
778e8d5648eSdan     abPK = (u8 *)pAlloc;
779ca62ad57Sdan     pAlloc = &abPK[nDbCol];
780e8d5648eSdan     if( pzTab ){
781e8d5648eSdan       memcpy(pAlloc, zThis, nThis+1);
782e8d5648eSdan       *pzTab = (char *)pAlloc;
783e8d5648eSdan       pAlloc += nThis+1;
784e8d5648eSdan     }
785e8d5648eSdan 
786e8d5648eSdan     i = 0;
787e8d5648eSdan     while( SQLITE_ROW==sqlite3_step(pStmt) ){
788e8d5648eSdan       int nName = sqlite3_column_bytes(pStmt, 1);
789e8d5648eSdan       const unsigned char *zName = sqlite3_column_text(pStmt, 1);
790e8d5648eSdan       if( zName==0 ) break;
791e8d5648eSdan       memcpy(pAlloc, zName, nName+1);
792e8d5648eSdan       azCol[i] = (char *)pAlloc;
793e8d5648eSdan       pAlloc += nName+1;
794db04571cSdan       abPK[i] = sqlite3_column_int(pStmt, 5);
795e8d5648eSdan       i++;
796e8d5648eSdan     }
797e8d5648eSdan     rc = sqlite3_reset(pStmt);
798e8d5648eSdan 
799e8d5648eSdan   }
800e8d5648eSdan 
801e8d5648eSdan   /* If successful, populate the output variables. Otherwise, zero them and
802e8d5648eSdan   ** free any allocation made. An error code will be returned in this case.
803e8d5648eSdan   */
804e8d5648eSdan   if( rc==SQLITE_OK ){
805db04571cSdan     *pazCol = (const char **)azCol;
806db04571cSdan     *pabPK = abPK;
807ca62ad57Sdan     *pnCol = nDbCol;
808e8d5648eSdan   }else{
809db04571cSdan     *pazCol = 0;
810db04571cSdan     *pabPK = 0;
811ca62ad57Sdan     *pnCol = 0;
812e8d5648eSdan     if( pzTab ) *pzTab = 0;
813db04571cSdan     sqlite3_free(azCol);
814e8d5648eSdan   }
815e8d5648eSdan   sqlite3_finalize(pStmt);
816e8d5648eSdan   return rc;
817e8d5648eSdan }
818e8d5648eSdan 
819e8d5648eSdan /*
820296c7658Sdan ** This function is only called from within a pre-update handler for a
821296c7658Sdan ** write to table pTab, part of session pSession. If this is the first
822296c7658Sdan ** write to this table, set the SessionTable.nCol variable to the number
823296c7658Sdan ** of columns in the table.
824296c7658Sdan **
825296c7658Sdan ** Otherwise, if this is not the first time this table has been written
826296c7658Sdan ** to, check that the number of columns in the table has not changed. If
827296c7658Sdan ** it has not, return zero.
828296c7658Sdan **
829296c7658Sdan ** If the number of columns in the table has changed since the last write
830296c7658Sdan ** was recorded, set the session error-code to SQLITE_SCHEMA and return
831296c7658Sdan ** non-zero. Users are not allowed to change the number of columns in a table
832296c7658Sdan ** for which changes are being recorded by the session module. If they do so,
833296c7658Sdan ** it is an error.
834296c7658Sdan */
8354fccf43aSdan static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
8364fccf43aSdan   if( pTab->nCol==0 ){
837e8d5648eSdan     assert( pTab->azCol==0 || pTab->abPK==0 );
838e8d5648eSdan     pSession->rc = sessionTableInfo(pSession->db, pSession->zDb,
839ca62ad57Sdan         pTab->zName, &pTab->nCol, 0, &pTab->azCol, &pTab->abPK
840e8d5648eSdan     );
841ca62ad57Sdan   }
842ca62ad57Sdan   if( pSession->rc==SQLITE_OK
843ca62ad57Sdan    && pTab->nCol!=sqlite3_preupdate_count(pSession->db)
844ca62ad57Sdan   ){
8454fccf43aSdan     pSession->rc = SQLITE_SCHEMA;
8464fccf43aSdan   }
847e8d5648eSdan   return pSession->rc;
848e8d5648eSdan }
849e8d5648eSdan 
850e8d5648eSdan static void sessionPreupdateOneChange(
851e8d5648eSdan   int op,
852e8d5648eSdan   sqlite3_session *pSession,
853e8d5648eSdan   SessionTable *pTab
854e8d5648eSdan ){
855e8d5648eSdan   sqlite3 *db = pSession->db;
856e8d5648eSdan   int iHash;
85727453faeSdan   int bNullPk = 0;
858e8d5648eSdan   int rc = SQLITE_OK;
859e8d5648eSdan 
860e8d5648eSdan   if( pSession->rc ) return;
861e8d5648eSdan 
862e8d5648eSdan   /* Load table details if required */
863e8d5648eSdan   if( sessionInitTable(pSession, pTab) ) return;
864e8d5648eSdan 
865e8d5648eSdan   /* Grow the hash table if required */
8665d607a6eSdan   if( sessionGrowHash(pTab) ){
8675d607a6eSdan     pSession->rc = SQLITE_NOMEM;
8685d607a6eSdan     return;
8695d607a6eSdan   }
870e8d5648eSdan 
871*80fe2d93Sdan   /* Calculate the hash-key for this change. If the primary key of the row
872*80fe2d93Sdan   ** includes a NULL value, exit early. Such changes are ignored by the
873*80fe2d93Sdan   ** session module. */
87427453faeSdan   rc = sessionPreupdateHash(db, pTab, op==SQLITE_INSERT, &iHash, &bNullPk);
875*80fe2d93Sdan   if( rc!=SQLITE_OK ) goto error_out;
876*80fe2d93Sdan 
877*80fe2d93Sdan   if( bNullPk==0 ){
878*80fe2d93Sdan     /* Search the hash table for an existing record for this row. */
879b4480e94Sdan     SessionChange *pC;
8806734007dSdan     for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){
881e8d5648eSdan       int bEqual;
8826734007dSdan       sessionPreupdateEqual(db, pTab, pC, op==SQLITE_INSERT, &bEqual);
883e8d5648eSdan       if( bEqual ) break;
884e8d5648eSdan     }
885*80fe2d93Sdan 
886e8d5648eSdan     if( pC==0 ){
887e8d5648eSdan       /* Create a new change object containing all the old values (if
888e8d5648eSdan       ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK
889e8d5648eSdan       ** values (if this is an INSERT). */
890b4480e94Sdan       SessionChange *pChange; /* New change object */
891e8d5648eSdan       int nByte;              /* Number of bytes to allocate */
892e8d5648eSdan       int i;                  /* Used to iterate through columns */
893e8d5648eSdan 
894b4480e94Sdan       assert( rc==SQLITE_OK );
895e8d5648eSdan       pTab->nEntry++;
896e8d5648eSdan 
897e8d5648eSdan       /* Figure out how large an allocation is required */
898e8d5648eSdan       nByte = sizeof(SessionChange);
899*80fe2d93Sdan       for(i=0; i<pTab->nCol; i++){
900e8d5648eSdan         sqlite3_value *p = 0;
901e8d5648eSdan         if( op!=SQLITE_INSERT ){
902*80fe2d93Sdan           TESTONLY(int trc = ) sqlite3_preupdate_old(pSession->db, i, &p);
903*80fe2d93Sdan           assert( trc==SQLITE_OK );
904*80fe2d93Sdan         }else if( pTab->abPK[i] ){
905*80fe2d93Sdan           TESTONLY(int trc = ) sqlite3_preupdate_new(pSession->db, i, &p);
906*80fe2d93Sdan           assert( trc==SQLITE_OK );
907e8d5648eSdan         }
908*80fe2d93Sdan 
909*80fe2d93Sdan         /* This may fail if SQLite value p contains a utf-16 string that must
910*80fe2d93Sdan         ** be converted to utf-8 and an OOM error occurs while doing so. */
911e8d5648eSdan         rc = sessionSerializeValue(0, p, &nByte);
912*80fe2d93Sdan         if( rc!=SQLITE_OK ) goto error_out;
913e8d5648eSdan       }
914e8d5648eSdan 
915e8d5648eSdan       /* Allocate the change object */
916e8d5648eSdan       pChange = (SessionChange *)sqlite3_malloc(nByte);
917e8d5648eSdan       if( !pChange ){
918e8d5648eSdan         rc = SQLITE_NOMEM;
919*80fe2d93Sdan         goto error_out;
920e8d5648eSdan       }else{
921e8d5648eSdan         memset(pChange, 0, sizeof(SessionChange));
922e8d5648eSdan         pChange->aRecord = (u8 *)&pChange[1];
923e8d5648eSdan       }
924e8d5648eSdan 
925*80fe2d93Sdan       /* Populate the change object. None of the preupdate_old(),
926*80fe2d93Sdan       ** preupdate_new() or SerializeValue() calls below may fail as all
927*80fe2d93Sdan       ** required values and encodings have already been cached in memory.
928*80fe2d93Sdan       ** It is not possible for an OOM to occur in this block. */
929e8d5648eSdan       nByte = 0;
930*80fe2d93Sdan       for(i=0; i<pTab->nCol; i++){
931e8d5648eSdan         sqlite3_value *p = 0;
932e8d5648eSdan         if( op!=SQLITE_INSERT ){
933*80fe2d93Sdan           sqlite3_preupdate_old(pSession->db, i, &p);
934*80fe2d93Sdan         }else if( pTab->abPK[i] ){
935*80fe2d93Sdan           sqlite3_preupdate_new(pSession->db, i, &p);
936e8d5648eSdan         }
937*80fe2d93Sdan         sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
938e8d5648eSdan       }
939*80fe2d93Sdan 
940*80fe2d93Sdan       /* Add the change to the hash-table */
941b4480e94Sdan       if( pSession->bIndirect || sqlite3_preupdate_depth(pSession->db) ){
942b4480e94Sdan         pChange->bIndirect = 1;
943b4480e94Sdan       }
94412ca0b56Sdan       pChange->nRecord = nByte;
945e8d5648eSdan       pChange->bInsert = (op==SQLITE_INSERT);
946e8d5648eSdan       pChange->pNext = pTab->apChange[iHash];
947e8d5648eSdan       pTab->apChange[iHash] = pChange;
948*80fe2d93Sdan 
949*80fe2d93Sdan     }else if( pC->bIndirect ){
950b4480e94Sdan       /* If the existing change is considered "indirect", but this current
951b4480e94Sdan       ** change is "direct", mark the change object as direct. */
952b4480e94Sdan       if( sqlite3_preupdate_depth(pSession->db)==0 && pSession->bIndirect==0 ){
953b4480e94Sdan         pC->bIndirect = 0;
954b4480e94Sdan       }
955e8d5648eSdan     }
9564fccf43aSdan   }
95712ca0b56Sdan 
95812ca0b56Sdan   /* If an error has occurred, mark the session object as failed. */
959*80fe2d93Sdan  error_out:
96012ca0b56Sdan   if( rc!=SQLITE_OK ){
96112ca0b56Sdan     pSession->rc = rc;
96212ca0b56Sdan   }
96327453faeSdan }
9644fccf43aSdan 
9654fccf43aSdan /*
9664fccf43aSdan ** The 'pre-update' hook registered by this module with SQLite databases.
9674fccf43aSdan */
9684fccf43aSdan static void xPreUpdate(
9694fccf43aSdan   void *pCtx,                     /* Copy of third arg to preupdate_hook() */
9704fccf43aSdan   sqlite3 *db,                    /* Database handle */
9714fccf43aSdan   int op,                         /* SQLITE_UPDATE, DELETE or INSERT */
9724fccf43aSdan   char const *zDb,                /* Database name */
9734fccf43aSdan   char const *zName,              /* Table name */
9744fccf43aSdan   sqlite3_int64 iKey1,            /* Rowid of row about to be deleted/updated */
9754fccf43aSdan   sqlite3_int64 iKey2             /* New rowid value (for a rowid UPDATE) */
9764fccf43aSdan ){
9774fccf43aSdan   sqlite3_session *pSession;
978cfdbde21Sdrh   int nDb = sqlite3Strlen30(zDb);
979cfdbde21Sdrh   int nName = sqlite3Strlen30(zName);
9804fccf43aSdan 
9814c220252Sdan   assert( sqlite3_mutex_held(db->mutex) );
9824c220252Sdan 
9834fccf43aSdan   for(pSession=(sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){
9844fccf43aSdan     SessionTable *pTab;
985296c7658Sdan 
986e8d5648eSdan     /* If this session is attached to a different database ("main", "temp"
987e8d5648eSdan     ** etc.), or if it is not currently enabled, there is nothing to do. Skip
988e8d5648eSdan     ** to the next session object attached to this database. */
989296c7658Sdan     if( pSession->bEnable==0 ) continue;
9904fccf43aSdan     if( pSession->rc ) continue;
9914fccf43aSdan     if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue;
992296c7658Sdan 
993ff4d0f41Sdan     for(pTab=pSession->pTable; pTab || pSession->bAutoAttach; pTab=pTab->pNext){
994ff4d0f41Sdan       if( !pTab ){
995ff4d0f41Sdan         /* This branch is taken if table zName has not yet been attached to
996ff4d0f41Sdan         ** this session and the auto-attach flag is set.  */
997ff4d0f41Sdan         pSession->rc = sqlite3session_attach(pSession,zName);
998245b49b2Sdan         if( pSession->rc ) break;
999ff4d0f41Sdan         pTab = pSession->pTable;
1000ff4d0f41Sdan         assert( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) );
1001ff4d0f41Sdan       }
1002ff4d0f41Sdan 
10034fccf43aSdan       if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ){
1004e8d5648eSdan         sessionPreupdateOneChange(op, pSession, pTab);
1005e8d5648eSdan         if( op==SQLITE_UPDATE ){
1006e8d5648eSdan           sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab);
10074fccf43aSdan         }
10084fccf43aSdan         break;
10094fccf43aSdan       }
10104fccf43aSdan     }
10114fccf43aSdan   }
1012296c7658Sdan }
10134fccf43aSdan 
10144fccf43aSdan /*
10154fccf43aSdan ** Create a session object. This session object will record changes to
10164fccf43aSdan ** database zDb attached to connection db.
10174fccf43aSdan */
10184fccf43aSdan int sqlite3session_create(
10194fccf43aSdan   sqlite3 *db,                    /* Database handle */
10204fccf43aSdan   const char *zDb,                /* Name of db (e.g. "main") */
10214fccf43aSdan   sqlite3_session **ppSession     /* OUT: New session object */
10224fccf43aSdan ){
1023296c7658Sdan   sqlite3_session *pNew;          /* Newly allocated session object */
1024296c7658Sdan   sqlite3_session *pOld;          /* Session object already attached to db */
1025cfdbde21Sdrh   int nDb = sqlite3Strlen30(zDb); /* Length of zDb in bytes */
10264fccf43aSdan 
1027296c7658Sdan   /* Zero the output value in case an error occurs. */
10284fccf43aSdan   *ppSession = 0;
10294fccf43aSdan 
10304fccf43aSdan   /* Allocate and populate the new session object. */
10314fccf43aSdan   pNew = (sqlite3_session *)sqlite3_malloc(sizeof(sqlite3_session) + nDb + 1);
10324fccf43aSdan   if( !pNew ) return SQLITE_NOMEM;
10334fccf43aSdan   memset(pNew, 0, sizeof(sqlite3_session));
10344fccf43aSdan   pNew->db = db;
10354fccf43aSdan   pNew->zDb = (char *)&pNew[1];
1036296c7658Sdan   pNew->bEnable = 1;
10374fccf43aSdan   memcpy(pNew->zDb, zDb, nDb+1);
10384fccf43aSdan 
10394fccf43aSdan   /* Add the new session object to the linked list of session objects
10404fccf43aSdan   ** attached to database handle $db. Do this under the cover of the db
10414fccf43aSdan   ** handle mutex.  */
10424fccf43aSdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
10434fccf43aSdan   pOld = (sqlite3_session*)sqlite3_preupdate_hook(db, xPreUpdate, (void*)pNew);
10444fccf43aSdan   pNew->pNext = pOld;
10454fccf43aSdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
10464fccf43aSdan 
10474fccf43aSdan   *ppSession = pNew;
10484fccf43aSdan   return SQLITE_OK;
10494fccf43aSdan }
10504fccf43aSdan 
10515d607a6eSdan void sessionDeleteTable(SessionTable *pList){
10525d607a6eSdan   SessionTable *pNext;
10535d607a6eSdan   SessionTable *pTab;
10545d607a6eSdan 
10555d607a6eSdan   for(pTab=pList; pTab; pTab=pNext){
10565d607a6eSdan     int i;
10575d607a6eSdan     pNext = pTab->pNext;
10585d607a6eSdan     for(i=0; i<pTab->nChange; i++){
10595d607a6eSdan       SessionChange *p;
10605d607a6eSdan       SessionChange *pNext;
10615d607a6eSdan       for(p=pTab->apChange[i]; p; p=pNext){
10625d607a6eSdan         pNext = p->pNext;
10635d607a6eSdan         sqlite3_free(p);
10645d607a6eSdan       }
10655d607a6eSdan     }
10665d607a6eSdan     sqlite3_free((char*)pTab->azCol);  /* cast works around VC++ bug */
10675d607a6eSdan     sqlite3_free(pTab->apChange);
10685d607a6eSdan     sqlite3_free(pTab);
10695d607a6eSdan   }
10705d607a6eSdan }
10715d607a6eSdan 
10724fccf43aSdan /*
10734fccf43aSdan ** Delete a session object previously allocated using sqlite3session_create().
10744fccf43aSdan */
10754fccf43aSdan void sqlite3session_delete(sqlite3_session *pSession){
10764fccf43aSdan   sqlite3 *db = pSession->db;
10774fccf43aSdan   sqlite3_session *pHead;
10784fccf43aSdan   sqlite3_session **pp;
10794fccf43aSdan 
1080296c7658Sdan   /* Unlink the session from the linked list of sessions attached to the
1081296c7658Sdan   ** database handle. Hold the db mutex while doing so.  */
10824fccf43aSdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
10834fccf43aSdan   pHead = (sqlite3_session*)sqlite3_preupdate_hook(db, 0, 0);
10844fccf43aSdan   for(pp=&pHead; (*pp)!=pSession; pp=&((*pp)->pNext));
10854fccf43aSdan   *pp = (*pp)->pNext;
10864fccf43aSdan   if( pHead ) sqlite3_preupdate_hook(db, xPreUpdate, (void *)pHead);
10874fccf43aSdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
10884fccf43aSdan 
1089296c7658Sdan   /* Delete all attached table objects. And the contents of their
1090296c7658Sdan   ** associated hash-tables. */
10915d607a6eSdan   sessionDeleteTable(pSession->pTable);
10924fccf43aSdan 
1093296c7658Sdan   /* Free the session object itself. */
10944fccf43aSdan   sqlite3_free(pSession);
10954fccf43aSdan }
10964fccf43aSdan 
10974fccf43aSdan /*
10984fccf43aSdan ** Attach a table to a session. All subsequent changes made to the table
10994fccf43aSdan ** while the session object is enabled will be recorded.
11004fccf43aSdan **
11014fccf43aSdan ** Only tables that have a PRIMARY KEY defined may be attached. It does
11024fccf43aSdan ** not matter if the PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias)
11034fccf43aSdan ** or not.
11044fccf43aSdan */
11054fccf43aSdan int sqlite3session_attach(
11064fccf43aSdan   sqlite3_session *pSession,      /* Session object */
11074fccf43aSdan   const char *zName               /* Table name */
11084fccf43aSdan ){
1109ff4d0f41Sdan   int rc = SQLITE_OK;
1110ff4d0f41Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
1111ff4d0f41Sdan 
1112ff4d0f41Sdan   if( !zName ){
1113ff4d0f41Sdan     pSession->bAutoAttach = 1;
1114ff4d0f41Sdan   }else{
1115296c7658Sdan     SessionTable *pTab;           /* New table object (if required) */
1116296c7658Sdan     int nName;                    /* Number of bytes in string zName */
11174fccf43aSdan 
11184fccf43aSdan     /* First search for an existing entry. If one is found, this call is
11194fccf43aSdan     ** a no-op. Return early. */
1120cfdbde21Sdrh     nName = sqlite3Strlen30(zName);
11214fccf43aSdan     for(pTab=pSession->pTable; pTab; pTab=pTab->pNext){
11224c220252Sdan       if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ) break;
11234fccf43aSdan     }
11244fccf43aSdan 
11254c220252Sdan     if( !pTab ){
11264fccf43aSdan       /* Allocate new SessionTable object. */
11274fccf43aSdan       pTab = (SessionTable *)sqlite3_malloc(sizeof(SessionTable) + nName + 1);
11284c220252Sdan       if( !pTab ){
11294c220252Sdan         rc = SQLITE_NOMEM;
11304c220252Sdan       }else{
11314fccf43aSdan         /* Populate the new SessionTable object and link it into the list. */
11324fccf43aSdan         memset(pTab, 0, sizeof(SessionTable));
11334fccf43aSdan         pTab->zName = (char *)&pTab[1];
11344fccf43aSdan         memcpy(pTab->zName, zName, nName+1);
11354fccf43aSdan         pTab->pNext = pSession->pTable;
11364fccf43aSdan         pSession->pTable = pTab;
11374c220252Sdan       }
11384c220252Sdan     }
1139ff4d0f41Sdan   }
11404fccf43aSdan 
11414c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
11424c220252Sdan   return rc;
11434fccf43aSdan }
11444fccf43aSdan 
1145296c7658Sdan /*
1146296c7658Sdan ** Ensure that there is room in the buffer to append nByte bytes of data.
1147296c7658Sdan ** If not, use sqlite3_realloc() to grow the buffer so that there is.
1148296c7658Sdan **
1149296c7658Sdan ** If successful, return zero. Otherwise, if an OOM condition is encountered,
1150296c7658Sdan ** set *pRc to SQLITE_NOMEM and return non-zero.
1151296c7658Sdan */
11524fccf43aSdan static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){
1153*80fe2d93Sdan   if( *pRc==SQLITE_OK && p->nAlloc-p->nBuf<nByte ){
11544fccf43aSdan     u8 *aNew;
11554fccf43aSdan     int nNew = p->nAlloc ? p->nAlloc : 128;
11564fccf43aSdan     do {
11574fccf43aSdan       nNew = nNew*2;
11584fccf43aSdan     }while( nNew<(p->nAlloc+nByte) );
11594fccf43aSdan 
11604fccf43aSdan     aNew = (u8 *)sqlite3_realloc(p->aBuf, nNew);
11614fccf43aSdan     if( 0==aNew ){
11624fccf43aSdan       *pRc = SQLITE_NOMEM;
1163*80fe2d93Sdan     }else{
11644fccf43aSdan       p->aBuf = aNew;
11654fccf43aSdan       p->nAlloc = nNew;
11664fccf43aSdan     }
1167*80fe2d93Sdan   }
1168*80fe2d93Sdan   return (*pRc!=SQLITE_OK);
11694fccf43aSdan }
11704fccf43aSdan 
1171296c7658Sdan /*
1172296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1173296c7658Sdan ** called. Otherwise, append a single byte to the buffer.
1174296c7658Sdan **
1175296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1176296c7658Sdan ** returning.
1177296c7658Sdan */
11784fccf43aSdan static void sessionAppendByte(SessionBuffer *p, u8 v, int *pRc){
1179*80fe2d93Sdan   if( 0==sessionBufferGrow(p, 1, pRc) ){
11804fccf43aSdan     p->aBuf[p->nBuf++] = v;
11814fccf43aSdan   }
11824fccf43aSdan }
11834fccf43aSdan 
1184296c7658Sdan /*
1185296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1186296c7658Sdan ** called. Otherwise, append a single varint to the buffer.
1187296c7658Sdan **
1188296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1189296c7658Sdan ** returning.
1190296c7658Sdan */
1191cfdbde21Sdrh static void sessionAppendVarint(SessionBuffer *p, int v, int *pRc){
1192*80fe2d93Sdan   if( 0==sessionBufferGrow(p, 9, pRc) ){
11934fccf43aSdan     p->nBuf += sessionVarintPut(&p->aBuf[p->nBuf], v);
11944fccf43aSdan   }
11954fccf43aSdan }
11964fccf43aSdan 
1197296c7658Sdan /*
1198296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1199296c7658Sdan ** called. Otherwise, append a blob of data to the buffer.
1200296c7658Sdan **
1201296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1202296c7658Sdan ** returning.
1203296c7658Sdan */
12044fccf43aSdan static void sessionAppendBlob(
12054fccf43aSdan   SessionBuffer *p,
12064fccf43aSdan   const u8 *aBlob,
12074fccf43aSdan   int nBlob,
12084fccf43aSdan   int *pRc
12094fccf43aSdan ){
1210*80fe2d93Sdan   if( 0==sessionBufferGrow(p, nBlob, pRc) ){
12114fccf43aSdan     memcpy(&p->aBuf[p->nBuf], aBlob, nBlob);
12124fccf43aSdan     p->nBuf += nBlob;
12134fccf43aSdan   }
12144fccf43aSdan }
12154fccf43aSdan 
1216296c7658Sdan /*
1217296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1218296c7658Sdan ** called. Otherwise, append a string to the buffer. All bytes in the string
1219296c7658Sdan ** up to (but not including) the nul-terminator are written to the buffer.
1220296c7658Sdan **
1221296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1222296c7658Sdan ** returning.
1223296c7658Sdan */
1224d5f0767cSdan static void sessionAppendStr(
1225d5f0767cSdan   SessionBuffer *p,
1226d5f0767cSdan   const char *zStr,
1227d5f0767cSdan   int *pRc
1228d5f0767cSdan ){
1229cfdbde21Sdrh   int nStr = sqlite3Strlen30(zStr);
1230*80fe2d93Sdan   if( 0==sessionBufferGrow(p, nStr, pRc) ){
1231d5f0767cSdan     memcpy(&p->aBuf[p->nBuf], zStr, nStr);
1232d5f0767cSdan     p->nBuf += nStr;
1233d5f0767cSdan   }
1234d5f0767cSdan }
1235d5f0767cSdan 
1236296c7658Sdan /*
1237296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1238296c7658Sdan ** called. Otherwise, append the string representation of integer iVal
1239296c7658Sdan ** to the buffer. No nul-terminator is written.
1240296c7658Sdan **
1241296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1242296c7658Sdan ** returning.
1243296c7658Sdan */
1244d5f0767cSdan static void sessionAppendInteger(
1245296c7658Sdan   SessionBuffer *p,               /* Buffer to append to */
1246296c7658Sdan   int iVal,                       /* Value to write the string rep. of */
1247296c7658Sdan   int *pRc                        /* IN/OUT: Error code */
1248d5f0767cSdan ){
1249d5f0767cSdan   char aBuf[24];
1250d5f0767cSdan   sqlite3_snprintf(sizeof(aBuf)-1, aBuf, "%d", iVal);
1251d5f0767cSdan   sessionAppendStr(p, aBuf, pRc);
1252d5f0767cSdan }
1253d5f0767cSdan 
1254296c7658Sdan /*
1255296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1256296c7658Sdan ** called. Otherwise, append the string zStr enclosed in quotes (") and
1257296c7658Sdan ** with any embedded quote characters escaped to the buffer. No
1258296c7658Sdan ** nul-terminator byte is written.
1259296c7658Sdan **
1260296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1261296c7658Sdan ** returning.
1262296c7658Sdan */
1263d5f0767cSdan static void sessionAppendIdent(
1264296c7658Sdan   SessionBuffer *p,               /* Buffer to a append to */
1265296c7658Sdan   const char *zStr,               /* String to quote, escape and append */
1266296c7658Sdan   int *pRc                        /* IN/OUT: Error code */
1267d5f0767cSdan ){
1268cfdbde21Sdrh   int nStr = sqlite3Strlen30(zStr)*2 + 2 + 1;
1269*80fe2d93Sdan   if( 0==sessionBufferGrow(p, nStr, pRc) ){
1270d5f0767cSdan     char *zOut = (char *)&p->aBuf[p->nBuf];
1271d5f0767cSdan     const char *zIn = zStr;
1272d5f0767cSdan     *zOut++ = '"';
1273d5f0767cSdan     while( *zIn ){
1274d5f0767cSdan       if( *zIn=='"' ) *zOut++ = '"';
1275d5f0767cSdan       *zOut++ = *(zIn++);
1276d5f0767cSdan     }
1277d5f0767cSdan     *zOut++ = '"';
1278cfdbde21Sdrh     p->nBuf = (int)((u8 *)zOut - p->aBuf);
1279d5f0767cSdan   }
1280d5f0767cSdan }
1281d5f0767cSdan 
1282296c7658Sdan /*
1283296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1284296c7658Sdan ** called. Otherwse, it appends the serialized version of the value stored
1285296c7658Sdan ** in column iCol of the row that SQL statement pStmt currently points
1286296c7658Sdan ** to to the buffer.
1287296c7658Sdan */
12884fccf43aSdan static void sessionAppendCol(
1289296c7658Sdan   SessionBuffer *p,               /* Buffer to append to */
1290296c7658Sdan   sqlite3_stmt *pStmt,            /* Handle pointing to row containing value */
1291296c7658Sdan   int iCol,                       /* Column to read value from */
1292296c7658Sdan   int *pRc                        /* IN/OUT: Error code */
12934fccf43aSdan ){
12944fccf43aSdan   if( *pRc==SQLITE_OK ){
12954fccf43aSdan     int eType = sqlite3_column_type(pStmt, iCol);
12964fccf43aSdan     sessionAppendByte(p, (u8)eType, pRc);
12974fccf43aSdan     if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
12984fccf43aSdan       sqlite3_int64 i;
12994fccf43aSdan       u8 aBuf[8];
13004fccf43aSdan       if( eType==SQLITE_INTEGER ){
13014fccf43aSdan         i = sqlite3_column_int64(pStmt, iCol);
13024fccf43aSdan       }else{
13034fccf43aSdan         double r = sqlite3_column_double(pStmt, iCol);
13044fccf43aSdan         memcpy(&i, &r, 8);
13054fccf43aSdan       }
1306296c7658Sdan       sessionPutI64(aBuf, i);
13074fccf43aSdan       sessionAppendBlob(p, aBuf, 8, pRc);
13084fccf43aSdan     }
13094fccf43aSdan     if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){
13106734007dSdan       u8 *z;
13116734007dSdan       if( eType==SQLITE_BLOB ){
13126734007dSdan         z = (u8 *)sqlite3_column_blob(pStmt, iCol);
13136734007dSdan       }else{
13146734007dSdan         z = (u8 *)sqlite3_column_text(pStmt, iCol);
13156734007dSdan       }
13166734007dSdan       if( z ){
13174fccf43aSdan         int nByte = sqlite3_column_bytes(pStmt, iCol);
13184fccf43aSdan         sessionAppendVarint(p, nByte, pRc);
13196734007dSdan         sessionAppendBlob(p, z, nByte, pRc);
13206734007dSdan       }else{
13216734007dSdan         *pRc = SQLITE_NOMEM;
13226734007dSdan       }
13234fccf43aSdan     }
13244fccf43aSdan   }
13254fccf43aSdan }
13264fccf43aSdan 
1327296c7658Sdan /*
1328296c7658Sdan **
1329*80fe2d93Sdan ** This function appends an update change to the buffer (see the comments
1330*80fe2d93Sdan ** under "CHANGESET FORMAT" at the top of the file). An update change
1331*80fe2d93Sdan ** consists of:
1332296c7658Sdan **
1333296c7658Sdan **   1 byte:  SQLITE_UPDATE (0x17)
1334296c7658Sdan **   n bytes: old.* record (see RECORD FORMAT)
1335296c7658Sdan **   m bytes: new.* record (see RECORD FORMAT)
1336296c7658Sdan **
1337296c7658Sdan ** The SessionChange object passed as the third argument contains the
1338296c7658Sdan ** values that were stored in the row when the session began (the old.*
1339296c7658Sdan ** values). The statement handle passed as the second argument points
1340296c7658Sdan ** at the current version of the row (the new.* values).
1341296c7658Sdan **
1342296c7658Sdan ** If all of the old.* values are equal to their corresponding new.* value
1343296c7658Sdan ** (i.e. nothing has changed), then no data at all is appended to the buffer.
1344296c7658Sdan **
1345296c7658Sdan ** Otherwise, the old.* record contains all primary key values and the
1346296c7658Sdan ** original values of any fields that have been modified. The new.* record
1347296c7658Sdan ** contains the new values of only those fields that have been modified.
1348296c7658Sdan */
1349*80fe2d93Sdan static int sessionAppendUpdate(
1350296c7658Sdan   SessionBuffer *pBuf,            /* Buffer to append to */
1351296c7658Sdan   sqlite3_stmt *pStmt,            /* Statement handle pointing at new row */
1352296c7658Sdan   SessionChange *p,               /* Object containing old values */
1353*80fe2d93Sdan   u8 *abPK                        /* Boolean array - true for PK columns */
13544fccf43aSdan ){
1355*80fe2d93Sdan   int rc = SQLITE_OK;
1356296c7658Sdan   SessionBuffer buf2 = {0,0,0}; /* Buffer to accumulate new.* record in */
1357296c7658Sdan   int bNoop = 1;                /* Set to zero if any values are modified */
13581f34f8ccSdan   int nRewind = pBuf->nBuf;     /* Set to zero if any values are modified */
1359296c7658Sdan   int i;                        /* Used to iterate through columns */
1360296c7658Sdan   u8 *pCsr = p->aRecord;        /* Used to iterate through old.* values */
1361296c7658Sdan 
1362*80fe2d93Sdan   sessionAppendByte(pBuf, SQLITE_UPDATE, &rc);
1363*80fe2d93Sdan   sessionAppendByte(pBuf, p->bIndirect, &rc);
13644fccf43aSdan   for(i=0; i<sqlite3_column_count(pStmt); i++){
136537f133ecSdan     int bChanged = 0;
13664fccf43aSdan     int nAdvance;
13674fccf43aSdan     int eType = *pCsr;
13684fccf43aSdan     switch( eType ){
13694fccf43aSdan       case SQLITE_NULL:
13704fccf43aSdan         nAdvance = 1;
13714fccf43aSdan         if( sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){
137237f133ecSdan           bChanged = 1;
13734fccf43aSdan         }
13744fccf43aSdan         break;
13754fccf43aSdan 
13764fccf43aSdan       case SQLITE_FLOAT:
13774fccf43aSdan       case SQLITE_INTEGER: {
13784fccf43aSdan         nAdvance = 9;
13794fccf43aSdan         if( eType==sqlite3_column_type(pStmt, i) ){
13804fccf43aSdan           sqlite3_int64 iVal = sessionGetI64(&pCsr[1]);
13814fccf43aSdan           if( eType==SQLITE_INTEGER ){
13824fccf43aSdan             if( iVal==sqlite3_column_int64(pStmt, i) ) break;
13834fccf43aSdan           }else{
13844fccf43aSdan             double dVal;
13854fccf43aSdan             memcpy(&dVal, &iVal, 8);
13864fccf43aSdan             if( dVal==sqlite3_column_double(pStmt, i) ) break;
13874fccf43aSdan           }
13884fccf43aSdan         }
138937f133ecSdan         bChanged = 1;
13904fccf43aSdan         break;
13914fccf43aSdan       }
13924fccf43aSdan 
1393e5754eecSdan       default: {
13944fccf43aSdan         int nByte;
13954fccf43aSdan         int nHdr = 1 + sessionVarintGet(&pCsr[1], &nByte);
1396e5754eecSdan         assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
13974fccf43aSdan         nAdvance = nHdr + nByte;
13984fccf43aSdan         if( eType==sqlite3_column_type(pStmt, i)
13994fccf43aSdan          && nByte==sqlite3_column_bytes(pStmt, i)
14004fccf43aSdan          && 0==memcmp(&pCsr[nHdr], sqlite3_column_blob(pStmt, i), nByte)
14014fccf43aSdan         ){
14024fccf43aSdan           break;
14034fccf43aSdan         }
140437f133ecSdan         bChanged = 1;
14054fccf43aSdan       }
14064fccf43aSdan     }
14074fccf43aSdan 
140837f133ecSdan     if( bChanged || abPK[i] ){
1409*80fe2d93Sdan       sessionAppendBlob(pBuf, pCsr, nAdvance, &rc);
14104fccf43aSdan     }else{
1411*80fe2d93Sdan       sessionAppendByte(pBuf, 0, &rc);
141237f133ecSdan     }
141337f133ecSdan 
141437f133ecSdan     if( bChanged ){
1415*80fe2d93Sdan       sessionAppendCol(&buf2, pStmt, i, &rc);
14164fccf43aSdan       bNoop = 0;
141737f133ecSdan     }else{
1418*80fe2d93Sdan       sessionAppendByte(&buf2, 0, &rc);
14194fccf43aSdan     }
142037f133ecSdan 
14214fccf43aSdan     pCsr += nAdvance;
14224fccf43aSdan   }
14234fccf43aSdan 
14244fccf43aSdan   if( bNoop ){
14251f34f8ccSdan     pBuf->nBuf = nRewind;
14264fccf43aSdan   }else{
1427*80fe2d93Sdan     sessionAppendBlob(pBuf, buf2.aBuf, buf2.nBuf, &rc);
14284fccf43aSdan   }
14291f34f8ccSdan   sqlite3_free(buf2.aBuf);
1430*80fe2d93Sdan 
1431*80fe2d93Sdan   return rc;
1432d5f0767cSdan }
14334fccf43aSdan 
1434e8d5648eSdan static int sessionSelectStmt(
1435e8d5648eSdan   sqlite3 *db,                    /* Database handle */
1436d7fb7d24Sdan   const char *zDb,                /* Database name */
1437e8d5648eSdan   const char *zTab,               /* Table name */
1438e8d5648eSdan   int nCol,
1439e8d5648eSdan   const char **azCol,
1440e8d5648eSdan   u8 *abPK,
1441e8d5648eSdan   sqlite3_stmt **ppStmt
1442d5f0767cSdan ){
1443e8d5648eSdan   int rc = SQLITE_OK;
1444d5f0767cSdan   int i;
1445e8d5648eSdan   const char *zSep = "";
1446e8d5648eSdan   SessionBuffer buf = {0, 0, 0};
1447d5f0767cSdan 
1448e8d5648eSdan   sessionAppendStr(&buf, "SELECT * FROM ", &rc);
1449d7fb7d24Sdan   sessionAppendIdent(&buf, zDb, &rc);
1450d7fb7d24Sdan   sessionAppendStr(&buf, ".", &rc);
1451e8d5648eSdan   sessionAppendIdent(&buf, zTab, &rc);
1452e8d5648eSdan   sessionAppendStr(&buf, " WHERE ", &rc);
1453e8d5648eSdan   for(i=0; i<nCol; i++){
1454e8d5648eSdan     if( abPK[i] ){
1455e8d5648eSdan       sessionAppendStr(&buf, zSep, &rc);
1456e8d5648eSdan       sessionAppendIdent(&buf, azCol[i], &rc);
1457e8d5648eSdan       sessionAppendStr(&buf, " = ?", &rc);
1458e8d5648eSdan       sessionAppendInteger(&buf, i+1, &rc);
1459e8d5648eSdan       zSep = " AND ";
1460d5f0767cSdan     }
1461d5f0767cSdan   }
1462d5f0767cSdan   if( rc==SQLITE_OK ){
1463e8d5648eSdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, ppStmt, 0);
1464d5f0767cSdan   }
1465e8d5648eSdan   sqlite3_free(buf.aBuf);
1466e8d5648eSdan   return rc;
1467d5f0767cSdan }
1468d5f0767cSdan 
1469e8d5648eSdan static int sessionSelectBind(
1470e8d5648eSdan   sqlite3_stmt *pSelect,
1471e8d5648eSdan   int nCol,
1472e8d5648eSdan   u8 *abPK,
1473e5754eecSdan   SessionChange *pChange
1474e8d5648eSdan ){
1475e8d5648eSdan   int i;
1476e8d5648eSdan   int rc = SQLITE_OK;
1477e5754eecSdan   u8 *a = pChange->aRecord;
1478d5f0767cSdan 
1479e8d5648eSdan   for(i=0; i<nCol && rc==SQLITE_OK; i++){
1480e8d5648eSdan     int eType = *a++;
1481e8d5648eSdan 
1482e8d5648eSdan     switch( eType ){
1483*80fe2d93Sdan       case 0:
1484e8d5648eSdan       case SQLITE_NULL:
1485e5754eecSdan         assert( abPK[i]==0 );
1486e8d5648eSdan         break;
1487e8d5648eSdan 
1488e8d5648eSdan       case SQLITE_INTEGER: {
1489e8d5648eSdan         if( abPK[i] ){
1490e8d5648eSdan           i64 iVal = sessionGetI64(a);
1491e8d5648eSdan           rc = sqlite3_bind_int64(pSelect, i+1, iVal);
1492e8d5648eSdan         }
1493e8d5648eSdan         a += 8;
1494e8d5648eSdan         break;
1495d5f0767cSdan       }
1496296c7658Sdan 
1497e8d5648eSdan       case SQLITE_FLOAT: {
1498e8d5648eSdan         if( abPK[i] ){
1499e8d5648eSdan           double rVal;
1500e8d5648eSdan           i64 iVal = sessionGetI64(a);
1501e8d5648eSdan           memcpy(&rVal, &iVal, 8);
15024e895da1Sdan           rc = sqlite3_bind_double(pSelect, i+1, rVal);
1503d5f0767cSdan         }
1504e8d5648eSdan         a += 8;
1505e8d5648eSdan         break;
1506e8d5648eSdan       }
1507e8d5648eSdan 
1508e8d5648eSdan       case SQLITE_TEXT: {
1509e8d5648eSdan         int n;
1510e8d5648eSdan         a += sessionVarintGet(a, &n);
1511e8d5648eSdan         if( abPK[i] ){
1512e8d5648eSdan           rc = sqlite3_bind_text(pSelect, i+1, (char *)a, n, SQLITE_TRANSIENT);
1513e8d5648eSdan         }
1514e8d5648eSdan         a += n;
1515e8d5648eSdan         break;
1516e8d5648eSdan       }
1517e8d5648eSdan 
1518e5754eecSdan       default: {
1519e8d5648eSdan         int n;
1520e5754eecSdan         assert( eType==SQLITE_BLOB );
1521e8d5648eSdan         a += sessionVarintGet(a, &n);
1522e8d5648eSdan         if( abPK[i] ){
1523e8d5648eSdan           rc = sqlite3_bind_blob(pSelect, i+1, a, n, SQLITE_TRANSIENT);
1524e8d5648eSdan         }
1525e8d5648eSdan         a += n;
1526e8d5648eSdan         break;
1527e8d5648eSdan       }
1528e8d5648eSdan     }
1529e8d5648eSdan   }
1530e8d5648eSdan 
1531d5f0767cSdan   return rc;
15324fccf43aSdan }
15334fccf43aSdan 
15345d607a6eSdan static void sessionAppendTableHdr(
15355d607a6eSdan   SessionBuffer *pBuf,
15365d607a6eSdan   SessionTable *pTab,
15375d607a6eSdan   int *pRc
15385d607a6eSdan ){
15395d607a6eSdan   /* Write a table header */
15405d607a6eSdan   sessionAppendByte(pBuf, 'T', pRc);
15415d607a6eSdan   sessionAppendVarint(pBuf, pTab->nCol, pRc);
15425d607a6eSdan   sessionAppendBlob(pBuf, pTab->abPK, pTab->nCol, pRc);
15435d607a6eSdan   sessionAppendBlob(pBuf, (u8 *)pTab->zName, strlen(pTab->zName)+1, pRc);
15445d607a6eSdan }
15455d607a6eSdan 
15464fccf43aSdan /*
15474fccf43aSdan ** Obtain a changeset object containing all changes recorded by the
15484fccf43aSdan ** session object passed as the first argument.
15494fccf43aSdan **
15504fccf43aSdan ** It is the responsibility of the caller to eventually free the buffer
15514fccf43aSdan ** using sqlite3_free().
15524fccf43aSdan */
15534fccf43aSdan int sqlite3session_changeset(
15544fccf43aSdan   sqlite3_session *pSession,      /* Session object */
15554fccf43aSdan   int *pnChangeset,               /* OUT: Size of buffer at *ppChangeset */
15564fccf43aSdan   void **ppChangeset              /* OUT: Buffer containing changeset */
15574fccf43aSdan ){
1558296c7658Sdan   sqlite3 *db = pSession->db;     /* Source database handle */
1559296c7658Sdan   SessionTable *pTab;             /* Used to iterate through attached tables */
1560296c7658Sdan   SessionBuffer buf = {0,0,0};    /* Buffer in which to accumlate changeset */
1561296c7658Sdan   int rc;                         /* Return code */
15624fccf43aSdan 
1563296c7658Sdan   /* Zero the output variables in case an error occurs. If this session
1564296c7658Sdan   ** object is already in the error state (sqlite3_session.rc != SQLITE_OK),
1565296c7658Sdan   ** this call will be a no-op.  */
15664fccf43aSdan   *pnChangeset = 0;
15674fccf43aSdan   *ppChangeset = 0;
1568e5754eecSdan 
1569e5754eecSdan   if( pSession->rc ) return pSession->rc;
1570e5754eecSdan   rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0);
1571e5754eecSdan   if( rc!=SQLITE_OK ) return rc;
1572e5754eecSdan 
1573e5754eecSdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
15744fccf43aSdan 
15754fccf43aSdan   for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
15764fccf43aSdan     if( pTab->nEntry ){
1577d7fb7d24Sdan       const char *zName = pTab->zName;
1578a9605b91Sdan       int nCol;                   /* Number of columns in table */
1579a9605b91Sdan       u8 *abPK;                   /* Primary key array */
1580a9605b91Sdan       const char **azCol = 0;     /* Table columns */
15811f34f8ccSdan       int i;                      /* Used to iterate through hash buckets */
15821f34f8ccSdan       sqlite3_stmt *pSel = 0;     /* SELECT statement to query table pTab */
15831f34f8ccSdan       int nRewind = buf.nBuf;     /* Initial size of write buffer */
15841f34f8ccSdan       int nNoop;                  /* Size of buffer after writing tbl header */
15854fccf43aSdan 
1586a9605b91Sdan       /* Check the table schema is still Ok. */
1587a9605b91Sdan       rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK);
1588a9605b91Sdan       if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){
1589a9605b91Sdan         rc = SQLITE_SCHEMA;
1590a9605b91Sdan       }
1591a9605b91Sdan 
15924fccf43aSdan       /* Write a table header */
15935d607a6eSdan       sessionAppendTableHdr(&buf, pTab, &rc);
15944fccf43aSdan 
15954fccf43aSdan       /* Build and compile a statement to execute: */
15964fccf43aSdan       if( rc==SQLITE_OK ){
1597d7fb7d24Sdan         rc = sessionSelectStmt(
1598a9605b91Sdan             db, pSession->zDb, zName, nCol, azCol, abPK, &pSel);
15994fccf43aSdan       }
16004fccf43aSdan 
16011f34f8ccSdan       nNoop = buf.nBuf;
160212ca0b56Sdan       for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
1603e8d5648eSdan         SessionChange *p;         /* Used to iterate through changes */
1604e8d5648eSdan 
16054fccf43aSdan         for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
1606e5754eecSdan           rc = sessionSelectBind(pSel, nCol, abPK, p);
1607*80fe2d93Sdan           if( rc!=SQLITE_OK ) continue;
16081f34f8ccSdan           if( sqlite3_step(pSel)==SQLITE_ROW ){
16094fccf43aSdan             int iCol;
1610e8d5648eSdan             if( p->bInsert ){
16114fccf43aSdan               sessionAppendByte(&buf, SQLITE_INSERT, &rc);
1612b4480e94Sdan               sessionAppendByte(&buf, p->bIndirect, &rc);
1613e8d5648eSdan               for(iCol=0; iCol<nCol; iCol++){
16141f34f8ccSdan                 sessionAppendCol(&buf, pSel, iCol, &rc);
16154fccf43aSdan               }
1616e8d5648eSdan             }else{
1617*80fe2d93Sdan               rc = sessionAppendUpdate(&buf, pSel, p, abPK);
16184fccf43aSdan             }
1619e8d5648eSdan           }else if( !p->bInsert ){
16204fccf43aSdan             /* A DELETE change */
16214fccf43aSdan             sessionAppendByte(&buf, SQLITE_DELETE, &rc);
1622b4480e94Sdan             sessionAppendByte(&buf, p->bIndirect, &rc);
16234fccf43aSdan             sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
16244fccf43aSdan           }
162512ca0b56Sdan           if( rc==SQLITE_OK ){
16261f34f8ccSdan             rc = sqlite3_reset(pSel);
16274fccf43aSdan           }
16284fccf43aSdan         }
1629e8d5648eSdan       }
16304fccf43aSdan 
16311f34f8ccSdan       sqlite3_finalize(pSel);
16321f34f8ccSdan       if( buf.nBuf==nNoop ){
16334fccf43aSdan         buf.nBuf = nRewind;
16344fccf43aSdan       }
1635cfdbde21Sdrh       sqlite3_free((char*)azCol);  /* cast works around VC++ bug */
16364fccf43aSdan     }
16374fccf43aSdan   }
16384fccf43aSdan 
16394fccf43aSdan   if( rc==SQLITE_OK ){
16404fccf43aSdan     *pnChangeset = buf.nBuf;
16414fccf43aSdan     *ppChangeset = buf.aBuf;
16424fccf43aSdan   }else{
16434fccf43aSdan     sqlite3_free(buf.aBuf);
16444fccf43aSdan   }
16454c220252Sdan 
1646e5754eecSdan   sqlite3_exec(db, "RELEASE changeset", 0, 0, 0);
16474c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
16484fccf43aSdan   return rc;
16494fccf43aSdan }
16504fccf43aSdan 
1651296c7658Sdan /*
1652296c7658Sdan ** Enable or disable the session object passed as the first argument.
1653296c7658Sdan */
16544fccf43aSdan int sqlite3session_enable(sqlite3_session *pSession, int bEnable){
16554c220252Sdan   int ret;
16564c220252Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
1657296c7658Sdan   if( bEnable>=0 ){
1658296c7658Sdan     pSession->bEnable = bEnable;
16594fccf43aSdan   }
16604c220252Sdan   ret = pSession->bEnable;
16614c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
16624c220252Sdan   return ret;
1663296c7658Sdan }
16644fccf43aSdan 
16654fccf43aSdan /*
1666b4480e94Sdan ** Enable or disable the session object passed as the first argument.
1667b4480e94Sdan */
1668b4480e94Sdan int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect){
1669b4480e94Sdan   int ret;
1670b4480e94Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
1671b4480e94Sdan   if( bIndirect>=0 ){
1672b4480e94Sdan     pSession->bIndirect = bIndirect;
1673b4480e94Sdan   }
1674b4480e94Sdan   ret = pSession->bIndirect;
1675b4480e94Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
1676b4480e94Sdan   return ret;
1677b4480e94Sdan }
1678b4480e94Sdan 
1679b4480e94Sdan /*
16804fccf43aSdan ** Create an iterator used to iterate through the contents of a changeset.
16814fccf43aSdan */
16824fccf43aSdan int sqlite3changeset_start(
1683296c7658Sdan   sqlite3_changeset_iter **pp,    /* OUT: Changeset iterator handle */
1684296c7658Sdan   int nChangeset,                 /* Size of buffer pChangeset in bytes */
1685296c7658Sdan   void *pChangeset                /* Pointer to buffer containing changeset */
16864fccf43aSdan ){
16874fccf43aSdan   sqlite3_changeset_iter *pRet;   /* Iterator to return */
16884fccf43aSdan   int nByte;                      /* Number of bytes to allocate for iterator */
16894fccf43aSdan 
1690296c7658Sdan   /* Zero the output variable in case an error occurs. */
1691296c7658Sdan   *pp = 0;
16924fccf43aSdan 
1693296c7658Sdan   /* Allocate and initialize the iterator structure. */
16944fccf43aSdan   nByte = sizeof(sqlite3_changeset_iter);
16954fccf43aSdan   pRet = (sqlite3_changeset_iter *)sqlite3_malloc(nByte);
16964fccf43aSdan   if( !pRet ) return SQLITE_NOMEM;
16974fccf43aSdan   memset(pRet, 0, sizeof(sqlite3_changeset_iter));
16984fccf43aSdan   pRet->aChangeset = (u8 *)pChangeset;
16994fccf43aSdan   pRet->nChangeset = nChangeset;
17004fccf43aSdan   pRet->pNext = pRet->aChangeset;
17014fccf43aSdan 
1702296c7658Sdan   /* Populate the output variable and return success. */
1703296c7658Sdan   *pp = pRet;
17044fccf43aSdan   return SQLITE_OK;
17054fccf43aSdan }
17064fccf43aSdan 
1707296c7658Sdan /*
1708296c7658Sdan ** Deserialize a single record from a buffer in memory. See "RECORD FORMAT"
1709296c7658Sdan ** for details.
1710296c7658Sdan **
1711296c7658Sdan ** When this function is called, *paChange points to the start of the record
1712296c7658Sdan ** to deserialize. Assuming no error occurs, *paChange is set to point to
1713296c7658Sdan ** one byte after the end of the same record before this function returns.
1714296c7658Sdan **
1715296c7658Sdan ** If successful, each element of the apOut[] array (allocated by the caller)
1716296c7658Sdan ** is set to point to an sqlite3_value object containing the value read
1717296c7658Sdan ** from the corresponding position in the record. If that value is not
1718296c7658Sdan ** included in the record (i.e. because the record is part of an UPDATE change
1719296c7658Sdan ** and the field was not modified), the corresponding element of apOut[] is
1720296c7658Sdan ** set to NULL.
1721296c7658Sdan **
1722296c7658Sdan ** It is the responsibility of the caller to free all sqlite_value structures
1723296c7658Sdan ** using sqlite3_free().
1724296c7658Sdan **
1725296c7658Sdan ** If an error occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
1726296c7658Sdan ** The apOut[] array may have been partially populated in this case.
1727296c7658Sdan */
17284fccf43aSdan static int sessionReadRecord(
17294fccf43aSdan   u8 **paChange,                  /* IN/OUT: Pointer to binary record */
17304fccf43aSdan   int nCol,                       /* Number of values in record */
17314fccf43aSdan   sqlite3_value **apOut           /* Write values to this array */
17324fccf43aSdan ){
1733296c7658Sdan   int i;                          /* Used to iterate through columns */
1734296c7658Sdan   u8 *aRec = *paChange;           /* Cursor for the serialized record */
17354fccf43aSdan 
17364fccf43aSdan   for(i=0; i<nCol; i++){
1737296c7658Sdan     int eType = *aRec++;          /* Type of value (SQLITE_NULL, TEXT etc.) */
173891ddd559Sdan     assert( !apOut || apOut[i]==0 );
17394fccf43aSdan     if( eType ){
174091ddd559Sdan       if( apOut ){
17414fccf43aSdan         apOut[i] = sqlite3ValueNew(0);
17424fccf43aSdan         if( !apOut[i] ) return SQLITE_NOMEM;
174391ddd559Sdan       }
17444fccf43aSdan 
17454fccf43aSdan       if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
17464fccf43aSdan         int nByte;
17474fccf43aSdan         aRec += sessionVarintGet(aRec, &nByte);
174891ddd559Sdan         if( apOut ){
17496734007dSdan           u8 enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0);
17506734007dSdan           sqlite3ValueSetStr(apOut[i], nByte, (char *)aRec, enc, SQLITE_STATIC);
175191ddd559Sdan         }
17524fccf43aSdan         aRec += nByte;
17534fccf43aSdan       }
17544fccf43aSdan       if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
175591ddd559Sdan         if( apOut ){
17564fccf43aSdan           sqlite3_int64 v = sessionGetI64(aRec);
17574fccf43aSdan           if( eType==SQLITE_INTEGER ){
17584fccf43aSdan             sqlite3VdbeMemSetInt64(apOut[i], v);
17594fccf43aSdan           }else{
17604fccf43aSdan             double d;
17614e895da1Sdan             memcpy(&d, &v, 8);
17624fccf43aSdan             sqlite3VdbeMemSetDouble(apOut[i], d);
17634fccf43aSdan           }
17644fccf43aSdan         }
176591ddd559Sdan         aRec += 8;
176691ddd559Sdan       }
17674fccf43aSdan     }
17684fccf43aSdan   }
17694fccf43aSdan 
17704fccf43aSdan   *paChange = aRec;
17714fccf43aSdan   return SQLITE_OK;
17724fccf43aSdan }
17734fccf43aSdan 
17745d607a6eSdan static int sessionChangesetNext(
17755d607a6eSdan   sqlite3_changeset_iter *p,
17765d607a6eSdan   u8 **paRec,
17775d607a6eSdan   int *pnRec
17785d607a6eSdan ){
17794fccf43aSdan   u8 *aChange;
17804fccf43aSdan   int i;
17814fccf43aSdan 
17825d607a6eSdan   assert( (paRec==0 && pnRec==0) || (paRec && pnRec) );
17835d607a6eSdan 
1784296c7658Sdan   /* If the iterator is in the error-state, return immediately. */
17854fccf43aSdan   if( p->rc!=SQLITE_OK ) return p->rc;
17864fccf43aSdan 
17875d607a6eSdan   /* Free the current contents of p->apValue[], if any. */
17884fccf43aSdan   if( p->apValue ){
17894fccf43aSdan     for(i=0; i<p->nCol*2; i++){
17904fccf43aSdan       sqlite3ValueFree(p->apValue[i]);
17914fccf43aSdan     }
17924fccf43aSdan     memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
17934fccf43aSdan   }
17944fccf43aSdan 
17954fccf43aSdan   /* If the iterator is already at the end of the changeset, return DONE. */
17964fccf43aSdan   if( p->pNext>=&p->aChangeset[p->nChangeset] ){
17974fccf43aSdan     return SQLITE_DONE;
17984fccf43aSdan   }
17994fccf43aSdan   aChange = p->pNext;
18004fccf43aSdan 
18015d607a6eSdan   if( aChange[0]=='T' ){
18024fccf43aSdan     int nByte;                    /* Bytes to allocate for apValue */
18035d607a6eSdan     aChange++;
18044fccf43aSdan     aChange += sessionVarintGet(aChange, &p->nCol);
1805244593c8Sdan     p->abPK = (u8 *)aChange;
1806244593c8Sdan     aChange += p->nCol;
18074fccf43aSdan     p->zTab = (char *)aChange;
1808cfdbde21Sdrh     aChange += (sqlite3Strlen30((char *)aChange) + 1);
18095d607a6eSdan 
18105d607a6eSdan     if( paRec==0 ){
18114fccf43aSdan       sqlite3_free(p->apValue);
18124fccf43aSdan       nByte = sizeof(sqlite3_value *) * p->nCol * 2;
18134fccf43aSdan       p->apValue = (sqlite3_value **)sqlite3_malloc(nByte);
18144fccf43aSdan       if( !p->apValue ){
18154fccf43aSdan         return (p->rc = SQLITE_NOMEM);
18164fccf43aSdan       }
18174fccf43aSdan       memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
18184fccf43aSdan     }
18195d607a6eSdan   }
18205d607a6eSdan 
18215d607a6eSdan   p->op = *(aChange++);
18225d607a6eSdan   p->bIndirect = *(aChange++);
18234fccf43aSdan   if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){
18244fccf43aSdan     return (p->rc = SQLITE_CORRUPT);
18254fccf43aSdan   }
18264fccf43aSdan 
18275d607a6eSdan   if( paRec ){ *paRec = aChange; }
18285d607a6eSdan 
18294fccf43aSdan   /* If this is an UPDATE or DELETE, read the old.* record. */
18304fccf43aSdan   if( p->op!=SQLITE_INSERT ){
18315d607a6eSdan     p->rc = sessionReadRecord(&aChange, p->nCol, paRec?0:p->apValue);
18324fccf43aSdan     if( p->rc!=SQLITE_OK ) return p->rc;
18334fccf43aSdan   }
18344fccf43aSdan 
18354fccf43aSdan   /* If this is an INSERT or UPDATE, read the new.* record. */
18364fccf43aSdan   if( p->op!=SQLITE_DELETE ){
18375d607a6eSdan     p->rc = sessionReadRecord(&aChange, p->nCol, paRec?0:&p->apValue[p->nCol]);
18384fccf43aSdan     if( p->rc!=SQLITE_OK ) return p->rc;
18394fccf43aSdan   }
18404fccf43aSdan 
18415d607a6eSdan   if( pnRec ){ *pnRec = aChange - *paRec; }
18424fccf43aSdan   p->pNext = aChange;
18434fccf43aSdan   return SQLITE_ROW;
18444fccf43aSdan }
18454fccf43aSdan 
18464fccf43aSdan /*
18475d607a6eSdan ** Advance an iterator created by sqlite3changeset_start() to the next
18485d607a6eSdan ** change in the changeset. This function may return SQLITE_ROW, SQLITE_DONE
18495d607a6eSdan ** or SQLITE_CORRUPT.
18505d607a6eSdan **
18515d607a6eSdan ** This function may not be called on iterators passed to a conflict handler
18525d607a6eSdan ** callback by changeset_apply().
18535d607a6eSdan */
18545d607a6eSdan int sqlite3changeset_next(sqlite3_changeset_iter *p){
18555d607a6eSdan   return sessionChangesetNext(p, 0, 0);
18565d607a6eSdan }
18575d607a6eSdan 
18585d607a6eSdan /*
1859244593c8Sdan ** The following function extracts information on the current change
18604fccf43aSdan ** from a changeset iterator. They may only be called after changeset_next()
18614fccf43aSdan ** has returned SQLITE_ROW.
18624fccf43aSdan */
18634fccf43aSdan int sqlite3changeset_op(
1864296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Iterator handle */
18654fccf43aSdan   const char **pzTab,             /* OUT: Pointer to table name */
18664fccf43aSdan   int *pnCol,                     /* OUT: Number of columns in table */
1867b4480e94Sdan   int *pOp,                       /* OUT: SQLITE_INSERT, DELETE or UPDATE */
1868b4480e94Sdan   int *pbIndirect                 /* OUT: True if change is indirect */
18694fccf43aSdan ){
18704fccf43aSdan   *pOp = pIter->op;
18714fccf43aSdan   *pnCol = pIter->nCol;
18724fccf43aSdan   *pzTab = pIter->zTab;
1873b4480e94Sdan   if( pbIndirect ) *pbIndirect = pIter->bIndirect;
18744fccf43aSdan   return SQLITE_OK;
18754fccf43aSdan }
18764fccf43aSdan 
1877244593c8Sdan int sqlite3changeset_pk(
1878244593c8Sdan   sqlite3_changeset_iter *pIter,  /* Iterator object */
1879244593c8Sdan   unsigned char **pabPK,          /* OUT: Array of boolean - true for PK cols */
1880244593c8Sdan   int *pnCol                      /* OUT: Number of entries in output array */
1881244593c8Sdan ){
1882244593c8Sdan   *pabPK = pIter->abPK;
1883244593c8Sdan   if( pnCol ) *pnCol = pIter->nCol;
1884244593c8Sdan   return SQLITE_OK;
1885244593c8Sdan }
1886244593c8Sdan 
1887296c7658Sdan /*
1888296c7658Sdan ** This function may only be called while the iterator is pointing to an
1889296c7658Sdan ** SQLITE_UPDATE or SQLITE_DELETE change (see sqlite3changeset_op()).
1890296c7658Sdan ** Otherwise, SQLITE_MISUSE is returned.
1891296c7658Sdan **
1892296c7658Sdan ** It sets *ppValue to point to an sqlite3_value structure containing the
1893296c7658Sdan ** iVal'th value in the old.* record. Or, if that particular value is not
1894296c7658Sdan ** included in the record (because the change is an UPDATE and the field
1895296c7658Sdan ** was not modified and is not a PK column), set *ppValue to NULL.
1896296c7658Sdan **
1897296c7658Sdan ** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is
1898296c7658Sdan ** not modified. Otherwise, SQLITE_OK.
1899296c7658Sdan */
19004fccf43aSdan int sqlite3changeset_old(
1901296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
1902296c7658Sdan   int iVal,                       /* Index of old.* value to retrieve */
19034fccf43aSdan   sqlite3_value **ppValue         /* OUT: Old value (or NULL pointer) */
19044fccf43aSdan ){
1905d5f0767cSdan   if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_DELETE ){
1906d5f0767cSdan     return SQLITE_MISUSE;
1907d5f0767cSdan   }
19084fccf43aSdan   if( iVal<0 || iVal>=pIter->nCol ){
19094fccf43aSdan     return SQLITE_RANGE;
19104fccf43aSdan   }
19114fccf43aSdan   *ppValue = pIter->apValue[iVal];
19124fccf43aSdan   return SQLITE_OK;
19134fccf43aSdan }
19144fccf43aSdan 
1915296c7658Sdan /*
1916296c7658Sdan ** This function may only be called while the iterator is pointing to an
1917296c7658Sdan ** SQLITE_UPDATE or SQLITE_INSERT change (see sqlite3changeset_op()).
1918296c7658Sdan ** Otherwise, SQLITE_MISUSE is returned.
1919296c7658Sdan **
1920296c7658Sdan ** It sets *ppValue to point to an sqlite3_value structure containing the
1921296c7658Sdan ** iVal'th value in the new.* record. Or, if that particular value is not
1922296c7658Sdan ** included in the record (because the change is an UPDATE and the field
1923296c7658Sdan ** was not modified), set *ppValue to NULL.
1924296c7658Sdan **
1925296c7658Sdan ** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is
1926296c7658Sdan ** not modified. Otherwise, SQLITE_OK.
1927296c7658Sdan */
19284fccf43aSdan int sqlite3changeset_new(
1929296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
1930296c7658Sdan   int iVal,                       /* Index of new.* value to retrieve */
19314fccf43aSdan   sqlite3_value **ppValue         /* OUT: New value (or NULL pointer) */
19324fccf43aSdan ){
1933d5f0767cSdan   if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_INSERT ){
1934d5f0767cSdan     return SQLITE_MISUSE;
1935d5f0767cSdan   }
19364fccf43aSdan   if( iVal<0 || iVal>=pIter->nCol ){
19374fccf43aSdan     return SQLITE_RANGE;
19384fccf43aSdan   }
19394fccf43aSdan   *ppValue = pIter->apValue[pIter->nCol+iVal];
19404fccf43aSdan   return SQLITE_OK;
19414fccf43aSdan }
19424fccf43aSdan 
1943296c7658Sdan /*
19447aa469cdSdan ** The following two macros are used internally. They are similar to the
19457aa469cdSdan ** sqlite3changeset_new() and sqlite3changeset_old() functions, except that
19467aa469cdSdan ** they omit all error checking and return a pointer to the requested value.
19477aa469cdSdan */
19487aa469cdSdan #define sessionChangesetNew(pIter, iVal) (pIter)->apValue[(pIter)->nCol+(iVal)]
19497aa469cdSdan #define sessionChangesetOld(pIter, iVal) (pIter)->apValue[(iVal)]
19507aa469cdSdan 
19517aa469cdSdan /*
1952296c7658Sdan ** This function may only be called with a changeset iterator that has been
1953296c7658Sdan ** passed to an SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT
1954296c7658Sdan ** conflict-handler function. Otherwise, SQLITE_MISUSE is returned.
1955296c7658Sdan **
1956296c7658Sdan ** If successful, *ppValue is set to point to an sqlite3_value structure
1957296c7658Sdan ** containing the iVal'th value of the conflicting record.
1958296c7658Sdan **
1959296c7658Sdan ** If value iVal is out-of-range or some other error occurs, an SQLite error
1960296c7658Sdan ** code is returned. Otherwise, SQLITE_OK.
1961296c7658Sdan */
1962d5f0767cSdan int sqlite3changeset_conflict(
1963296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
1964296c7658Sdan   int iVal,                       /* Index of conflict record value to fetch */
1965d5f0767cSdan   sqlite3_value **ppValue         /* OUT: Value from conflicting row */
1966d5f0767cSdan ){
1967d5f0767cSdan   if( !pIter->pConflict ){
1968d5f0767cSdan     return SQLITE_MISUSE;
1969d5f0767cSdan   }
1970d5f0767cSdan   if( iVal<0 || iVal>=sqlite3_column_count(pIter->pConflict) ){
1971d5f0767cSdan     return SQLITE_RANGE;
1972d5f0767cSdan   }
1973d5f0767cSdan   *ppValue = sqlite3_column_value(pIter->pConflict, iVal);
1974d5f0767cSdan   return SQLITE_OK;
1975d5f0767cSdan }
1976d5f0767cSdan 
19774fccf43aSdan /*
19784fccf43aSdan ** Finalize an iterator allocated with sqlite3changeset_start().
19794fccf43aSdan **
19804fccf43aSdan ** This function may not be called on iterators passed to a conflict handler
19814fccf43aSdan ** callback by changeset_apply().
19824fccf43aSdan */
19834fccf43aSdan int sqlite3changeset_finalize(sqlite3_changeset_iter *p){
1984296c7658Sdan   int i;                          /* Used to iterate through p->apValue[] */
1985296c7658Sdan   int rc = p->rc;                 /* Return code */
198612ca0b56Sdan   if( p->apValue ){
19874fccf43aSdan     for(i=0; i<p->nCol*2; i++) sqlite3ValueFree(p->apValue[i]);
198812ca0b56Sdan   }
19894fccf43aSdan   sqlite3_free(p->apValue);
19904fccf43aSdan   sqlite3_free(p);
19914fccf43aSdan   return rc;
19924fccf43aSdan }
19934fccf43aSdan 
199491ddd559Sdan /*
199591ddd559Sdan ** Invert a changeset object.
199691ddd559Sdan */
199791ddd559Sdan int sqlite3changeset_invert(
199891ddd559Sdan   int nChangeset,                 /* Number of bytes in input */
199991ddd559Sdan   void *pChangeset,               /* Input changeset */
200091ddd559Sdan   int *pnInverted,                /* OUT: Number of bytes in output changeset */
200191ddd559Sdan   void **ppInverted               /* OUT: Inverse of pChangeset */
200291ddd559Sdan ){
200391ddd559Sdan   u8 *aOut;
200491ddd559Sdan   u8 *aIn;
200591ddd559Sdan   int i;
200691ddd559Sdan   int nCol = 0;
200791ddd559Sdan 
200891ddd559Sdan   /* Zero the output variables in case an error occurs. */
200991ddd559Sdan   *ppInverted = 0;
201091ddd559Sdan   *pnInverted = 0;
201191ddd559Sdan   if( nChangeset==0 ) return SQLITE_OK;
201291ddd559Sdan 
201391ddd559Sdan   aOut = (u8 *)sqlite3_malloc(nChangeset);
201491ddd559Sdan   if( !aOut ) return SQLITE_NOMEM;
201591ddd559Sdan   aIn = (u8 *)pChangeset;
201691ddd559Sdan 
201791ddd559Sdan   i = 0;
201891ddd559Sdan   while( i<nChangeset ){
201991ddd559Sdan     u8 eType = aIn[i];
202091ddd559Sdan     switch( eType ){
202191ddd559Sdan       case 'T': {
2022244593c8Sdan         /* A 'table' record consists of:
2023244593c8Sdan         **
2024244593c8Sdan         **   * A constant 'T' character,
2025244593c8Sdan         **   * Number of columns in said table (a varint),
2026244593c8Sdan         **   * An array of nCol bytes (abPK),
2027244593c8Sdan         **   * A nul-terminated table name.
2028244593c8Sdan         */
202991ddd559Sdan         int nByte = 1 + sessionVarintGet(&aIn[i+1], &nCol);
2030244593c8Sdan         nByte += nCol;
2031cfdbde21Sdrh         nByte += 1 + sqlite3Strlen30((char *)&aIn[i+nByte]);
203291ddd559Sdan         memcpy(&aOut[i], &aIn[i], nByte);
203391ddd559Sdan         i += nByte;
203491ddd559Sdan         break;
203591ddd559Sdan       }
203691ddd559Sdan 
203791ddd559Sdan       case SQLITE_INSERT:
203891ddd559Sdan       case SQLITE_DELETE: {
203991ddd559Sdan         int nByte;
2040b4480e94Sdan         u8 *aEnd = &aIn[i+2];
204191ddd559Sdan 
204291ddd559Sdan         sessionReadRecord(&aEnd, nCol, 0);
204391ddd559Sdan         aOut[i] = (eType==SQLITE_DELETE ? SQLITE_INSERT : SQLITE_DELETE);
2044b4480e94Sdan         aOut[i+1] = aIn[i+1];
2045cfdbde21Sdrh         nByte = (int)(aEnd - &aIn[i+2]);
2046b4480e94Sdan         memcpy(&aOut[i+2], &aIn[i+2], nByte);
2047b4480e94Sdan         i += 2 + nByte;
204891ddd559Sdan         break;
204991ddd559Sdan       }
205091ddd559Sdan 
205191ddd559Sdan       case SQLITE_UPDATE: {
205291ddd559Sdan         int nByte1;              /* Size of old.* record in bytes */
205391ddd559Sdan         int nByte2;              /* Size of new.* record in bytes */
2054b4480e94Sdan         u8 *aEnd = &aIn[i+2];
205591ddd559Sdan 
205691ddd559Sdan         sessionReadRecord(&aEnd, nCol, 0);
2057cfdbde21Sdrh         nByte1 = (int)(aEnd - &aIn[i+2]);
205891ddd559Sdan         sessionReadRecord(&aEnd, nCol, 0);
2059cfdbde21Sdrh         nByte2 = (int)(aEnd - &aIn[i+2]) - nByte1;
206091ddd559Sdan 
206191ddd559Sdan         aOut[i] = SQLITE_UPDATE;
2062b4480e94Sdan         aOut[i+1] = aIn[i+1];
2063b4480e94Sdan         memcpy(&aOut[i+2], &aIn[i+2+nByte1], nByte2);
2064b4480e94Sdan         memcpy(&aOut[i+2+nByte2], &aIn[i+2], nByte1);
206591ddd559Sdan 
2066b4480e94Sdan         i += 2 + nByte1 + nByte2;
206791ddd559Sdan         break;
206891ddd559Sdan       }
206991ddd559Sdan 
207091ddd559Sdan       default:
207191ddd559Sdan         sqlite3_free(aOut);
207291ddd559Sdan         return SQLITE_CORRUPT;
207391ddd559Sdan     }
207491ddd559Sdan   }
207591ddd559Sdan 
207691ddd559Sdan   *pnInverted = nChangeset;
207791ddd559Sdan   *ppInverted = (void *)aOut;
207891ddd559Sdan   return SQLITE_OK;
207991ddd559Sdan }
208091ddd559Sdan 
20810c698471Sdan typedef struct SessionApplyCtx SessionApplyCtx;
20820c698471Sdan struct SessionApplyCtx {
20830c698471Sdan   sqlite3 *db;
20840c698471Sdan   sqlite3_stmt *pDelete;          /* DELETE statement */
20850c698471Sdan   sqlite3_stmt *pUpdate;          /* DELETE statement */
20860c698471Sdan   sqlite3_stmt *pInsert;          /* INSERT statement */
20870c698471Sdan   sqlite3_stmt *pSelect;          /* SELECT statement */
20880c698471Sdan   int nCol;                       /* Size of azCol[] and abPK[] arrays */
20890c698471Sdan   const char **azCol;             /* Array of column names */
20900c698471Sdan   u8 *abPK;                       /* Boolean array - true if column is in PK */
20910c698471Sdan };
20920c698471Sdan 
2093d5f0767cSdan /*
2094d5f0767cSdan ** Formulate a statement to DELETE a row from database db. Assuming a table
2095d5f0767cSdan ** structure like this:
2096d5f0767cSdan **
2097d5f0767cSdan **     CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
2098d5f0767cSdan **
2099d5f0767cSdan ** The DELETE statement looks like this:
2100d5f0767cSdan **
2101db04571cSdan **     DELETE FROM x WHERE a = :1 AND c = :3 AND (:5 OR b IS :2 AND d IS :4)
2102d5f0767cSdan **
2103d5f0767cSdan ** Variable :5 (nCol+1) is a boolean. It should be set to 0 if we require
2104d5f0767cSdan ** matching b and d values, or 1 otherwise. The second case comes up if the
2105d5f0767cSdan ** conflict handler is invoked with NOTFOUND and returns CHANGESET_REPLACE.
2106296c7658Sdan **
2107296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pDelete is left
2108296c7658Sdan ** pointing to the prepared version of the SQL statement.
2109d5f0767cSdan */
2110d5f0767cSdan static int sessionDeleteRow(
2111d5f0767cSdan   sqlite3 *db,                    /* Database handle */
2112d5f0767cSdan   const char *zTab,               /* Table name */
21130c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
2114d5f0767cSdan ){
2115296c7658Sdan   int i;
2116296c7658Sdan   const char *zSep = "";
2117d5f0767cSdan   int rc = SQLITE_OK;
2118d5f0767cSdan   SessionBuffer buf = {0, 0, 0};
21197cf7df7dSdan   int nPk = 0;
2120d5f0767cSdan 
2121d5f0767cSdan   sessionAppendStr(&buf, "DELETE FROM ", &rc);
2122d5f0767cSdan   sessionAppendIdent(&buf, zTab, &rc);
2123296c7658Sdan   sessionAppendStr(&buf, " WHERE ", &rc);
2124296c7658Sdan 
2125296c7658Sdan   for(i=0; i<p->nCol; i++){
2126296c7658Sdan     if( p->abPK[i] ){
21277cf7df7dSdan       nPk++;
2128296c7658Sdan       sessionAppendStr(&buf, zSep, &rc);
2129296c7658Sdan       sessionAppendIdent(&buf, p->azCol[i], &rc);
2130296c7658Sdan       sessionAppendStr(&buf, " = ?", &rc);
2131296c7658Sdan       sessionAppendInteger(&buf, i+1, &rc);
2132296c7658Sdan       zSep = " AND ";
2133296c7658Sdan     }
2134296c7658Sdan   }
2135296c7658Sdan 
21367cf7df7dSdan   if( nPk<p->nCol ){
2137296c7658Sdan     sessionAppendStr(&buf, " AND (?", &rc);
2138296c7658Sdan     sessionAppendInteger(&buf, p->nCol+1, &rc);
2139296c7658Sdan     sessionAppendStr(&buf, " OR ", &rc);
2140296c7658Sdan 
2141296c7658Sdan     zSep = "";
2142296c7658Sdan     for(i=0; i<p->nCol; i++){
2143296c7658Sdan       if( !p->abPK[i] ){
2144296c7658Sdan         sessionAppendStr(&buf, zSep, &rc);
2145296c7658Sdan         sessionAppendIdent(&buf, p->azCol[i], &rc);
2146296c7658Sdan         sessionAppendStr(&buf, " IS ?", &rc);
2147296c7658Sdan         sessionAppendInteger(&buf, i+1, &rc);
2148296c7658Sdan         zSep = "AND ";
2149296c7658Sdan       }
2150296c7658Sdan     }
2151296c7658Sdan     sessionAppendStr(&buf, ")", &rc);
21527cf7df7dSdan   }
2153d5f0767cSdan 
2154d5f0767cSdan   if( rc==SQLITE_OK ){
21550c698471Sdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pDelete, 0);
2156d5f0767cSdan   }
2157d5f0767cSdan   sqlite3_free(buf.aBuf);
2158d5f0767cSdan 
2159d5f0767cSdan   return rc;
2160d5f0767cSdan }
2161d5f0767cSdan 
2162d5f0767cSdan /*
2163d5f0767cSdan ** Formulate and prepare a statement to UPDATE a row from database db.
2164d5f0767cSdan ** Assuming a table structure like this:
2165d5f0767cSdan **
2166d5f0767cSdan **     CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
2167d5f0767cSdan **
2168d5f0767cSdan ** The UPDATE statement looks like this:
2169d5f0767cSdan **
2170d5f0767cSdan **     UPDATE x SET
2171d5f0767cSdan **     a = CASE WHEN ?2  THEN ?3  ELSE a END,
2172d5f0767cSdan **     b = CASE WHEN ?5  THEN ?6  ELSE a END,
2173d5f0767cSdan **     c = CASE WHEN ?8  THEN ?9  ELSE a END,
2174d5f0767cSdan **     d = CASE WHEN ?11 THEN ?12 ELSE a END
2175d5f0767cSdan **     WHERE a = ?1 AND c = ?7 AND (?13 OR
2176d5f0767cSdan **       (?5==0 OR b IS ?4) AND (?11==0 OR b IS ?10) AND
2177d5f0767cSdan **     )
2178d5f0767cSdan **
2179d5f0767cSdan ** For each column in the table, there are three variables to bind:
2180d5f0767cSdan **
2181d5f0767cSdan **     ?(i*3+1)    The old.* value of the column, if any.
2182d5f0767cSdan **     ?(i*3+2)    A boolean flag indicating that the value is being modified.
2183d5f0767cSdan **     ?(i*3+3)    The new.* value of the column, if any.
2184d5f0767cSdan **
2185d5f0767cSdan ** Also, a boolean flag that, if set to true, causes the statement to update
2186d5f0767cSdan ** a row even if the non-PK values do not match. This is required if the
2187d5f0767cSdan ** conflict-handler is invoked with CHANGESET_DATA and returns
2188d5f0767cSdan ** CHANGESET_REPLACE. This is variable "?(nCol*3+1)".
2189d5f0767cSdan **
2190296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left
2191296c7658Sdan ** pointing to the prepared version of the SQL statement.
2192d5f0767cSdan */
2193d5f0767cSdan static int sessionUpdateRow(
2194d5f0767cSdan   sqlite3 *db,                    /* Database handle */
2195d5f0767cSdan   const char *zTab,               /* Table name */
21960c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
2197d5f0767cSdan ){
2198d5f0767cSdan   int rc = SQLITE_OK;
2199d5f0767cSdan   int i;
2200d5f0767cSdan   const char *zSep = "";
2201d5f0767cSdan   SessionBuffer buf = {0, 0, 0};
2202d5f0767cSdan 
2203d5f0767cSdan   /* Append "UPDATE tbl SET " */
2204d5f0767cSdan   sessionAppendStr(&buf, "UPDATE ", &rc);
2205d5f0767cSdan   sessionAppendIdent(&buf, zTab, &rc);
2206d5f0767cSdan   sessionAppendStr(&buf, " SET ", &rc);
2207d5f0767cSdan 
2208d5f0767cSdan   /* Append the assignments */
22090c698471Sdan   for(i=0; i<p->nCol; i++){
2210d5f0767cSdan     sessionAppendStr(&buf, zSep, &rc);
22110c698471Sdan     sessionAppendIdent(&buf, p->azCol[i], &rc);
2212d5f0767cSdan     sessionAppendStr(&buf, " = CASE WHEN ?", &rc);
2213d5f0767cSdan     sessionAppendInteger(&buf, i*3+2, &rc);
2214d5f0767cSdan     sessionAppendStr(&buf, " THEN ?", &rc);
2215d5f0767cSdan     sessionAppendInteger(&buf, i*3+3, &rc);
2216d5f0767cSdan     sessionAppendStr(&buf, " ELSE ", &rc);
22170c698471Sdan     sessionAppendIdent(&buf, p->azCol[i], &rc);
2218d5f0767cSdan     sessionAppendStr(&buf, " END", &rc);
2219d5f0767cSdan     zSep = ", ";
2220d5f0767cSdan   }
2221d5f0767cSdan 
2222d5f0767cSdan   /* Append the PK part of the WHERE clause */
2223d5f0767cSdan   sessionAppendStr(&buf, " WHERE ", &rc);
22240c698471Sdan   for(i=0; i<p->nCol; i++){
22250c698471Sdan     if( p->abPK[i] ){
22260c698471Sdan       sessionAppendIdent(&buf, p->azCol[i], &rc);
2227d5f0767cSdan       sessionAppendStr(&buf, " = ?", &rc);
2228d5f0767cSdan       sessionAppendInteger(&buf, i*3+1, &rc);
2229d5f0767cSdan       sessionAppendStr(&buf, " AND ", &rc);
2230d5f0767cSdan     }
2231d5f0767cSdan   }
2232d5f0767cSdan 
2233d5f0767cSdan   /* Append the non-PK part of the WHERE clause */
2234d5f0767cSdan   sessionAppendStr(&buf, " (?", &rc);
22350c698471Sdan   sessionAppendInteger(&buf, p->nCol*3+1, &rc);
2236d5f0767cSdan   sessionAppendStr(&buf, " OR 1", &rc);
22370c698471Sdan   for(i=0; i<p->nCol; i++){
22380c698471Sdan     if( !p->abPK[i] ){
2239d5f0767cSdan       sessionAppendStr(&buf, " AND (?", &rc);
2240d5f0767cSdan       sessionAppendInteger(&buf, i*3+2, &rc);
2241d5f0767cSdan       sessionAppendStr(&buf, "=0 OR ", &rc);
22420c698471Sdan       sessionAppendIdent(&buf, p->azCol[i], &rc);
2243d5f0767cSdan       sessionAppendStr(&buf, " IS ?", &rc);
2244d5f0767cSdan       sessionAppendInteger(&buf, i*3+1, &rc);
2245d5f0767cSdan       sessionAppendStr(&buf, ")", &rc);
2246d5f0767cSdan     }
2247d5f0767cSdan   }
2248d5f0767cSdan   sessionAppendStr(&buf, ")", &rc);
2249d5f0767cSdan 
2250d5f0767cSdan   if( rc==SQLITE_OK ){
22510c698471Sdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0);
2252d5f0767cSdan   }
2253d5f0767cSdan   sqlite3_free(buf.aBuf);
2254d5f0767cSdan 
2255d5f0767cSdan   return rc;
2256d5f0767cSdan }
2257d5f0767cSdan 
2258296c7658Sdan /*
2259296c7658Sdan ** Formulate and prepare an SQL statement to query table zTab by primary
2260296c7658Sdan ** key. Assuming the following table structure:
2261296c7658Sdan **
2262296c7658Sdan **     CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
2263296c7658Sdan **
2264296c7658Sdan ** The SELECT statement looks like this:
2265296c7658Sdan **
2266296c7658Sdan **     SELECT * FROM x WHERE a = ?1 AND c = ?3
2267296c7658Sdan **
2268296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pSelect is left
2269296c7658Sdan ** pointing to the prepared version of the SQL statement.
2270296c7658Sdan */
2271d5f0767cSdan static int sessionSelectRow(
2272d5f0767cSdan   sqlite3 *db,                    /* Database handle */
2273d5f0767cSdan   const char *zTab,               /* Table name */
22740c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
2275d5f0767cSdan ){
2276d7fb7d24Sdan   return sessionSelectStmt(
2277d7fb7d24Sdan       db, "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect);
2278d5f0767cSdan }
2279d5f0767cSdan 
2280296c7658Sdan /*
2281296c7658Sdan ** Formulate and prepare an INSERT statement to add a record to table zTab.
2282296c7658Sdan ** For example:
2283296c7658Sdan **
2284296c7658Sdan **     INSERT INTO main."zTab" VALUES(?1, ?2, ?3 ...);
2285296c7658Sdan **
2286296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pInsert is left
2287296c7658Sdan ** pointing to the prepared version of the SQL statement.
2288296c7658Sdan */
22890c698471Sdan static int sessionInsertRow(
22900c698471Sdan   sqlite3 *db,                    /* Database handle */
22910c698471Sdan   const char *zTab,               /* Table name */
22920c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
22930c698471Sdan ){
22940c698471Sdan   int rc = SQLITE_OK;
22950c698471Sdan   int i;
22960c698471Sdan   SessionBuffer buf = {0, 0, 0};
22970c698471Sdan 
22980c698471Sdan   sessionAppendStr(&buf, "INSERT INTO main.", &rc);
22990c698471Sdan   sessionAppendIdent(&buf, zTab, &rc);
23000c698471Sdan   sessionAppendStr(&buf, " VALUES(?", &rc);
23010c698471Sdan   for(i=1; i<p->nCol; i++){
23020c698471Sdan     sessionAppendStr(&buf, ", ?", &rc);
23030c698471Sdan   }
23040c698471Sdan   sessionAppendStr(&buf, ")", &rc);
23050c698471Sdan 
23060c698471Sdan   if( rc==SQLITE_OK ){
23070c698471Sdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pInsert, 0);
23080c698471Sdan   }
23090c698471Sdan   sqlite3_free(buf.aBuf);
23100c698471Sdan   return rc;
23110c698471Sdan }
23120c698471Sdan 
2313296c7658Sdan /*
23147aa469cdSdan ** A wrapper around sqlite3_bind_value() that detects an extra problem.
23157aa469cdSdan ** See comments in the body of this function for details.
23167aa469cdSdan */
23177aa469cdSdan static int sessionBindValue(
23187aa469cdSdan   sqlite3_stmt *pStmt,            /* Statement to bind value to */
23197aa469cdSdan   int i,                          /* Parameter number to bind to */
23207aa469cdSdan   sqlite3_value *pVal             /* Value to bind */
23217aa469cdSdan ){
23227aa469cdSdan   if( (pVal->type==SQLITE_TEXT || pVal->type==SQLITE_BLOB) && pVal->z==0 ){
23237aa469cdSdan     /* This condition occurs when an earlier OOM in a call to
23247aa469cdSdan     ** sqlite3_value_text() or sqlite3_value_blob() (perhaps from within
23257aa469cdSdan     ** a conflict-hanler) has zeroed the pVal->z pointer. Return NOMEM. */
23267aa469cdSdan     return SQLITE_NOMEM;
23277aa469cdSdan   }
23287aa469cdSdan   return sqlite3_bind_value(pStmt, i, pVal);
23297aa469cdSdan }
23307aa469cdSdan 
23317aa469cdSdan /*
2332db04571cSdan ** Iterator pIter must point to an SQLITE_INSERT entry. This function
2333db04571cSdan ** transfers new.* values from the current iterator entry to statement
2334db04571cSdan ** pStmt. The table being inserted into has nCol columns.
2335db04571cSdan **
2336db04571cSdan ** New.* value $i 0 from the iterator is bound to variable ($i+1) of
2337db04571cSdan ** statement pStmt. If parameter abPK is NULL, all values from 0 to (nCol-1)
2338db04571cSdan ** are transfered to the statement. Otherwise, if abPK is not NULL, it points
2339db04571cSdan ** to an array nCol elements in size. In this case only those values for
2340db04571cSdan ** which abPK[$i] is true are read from the iterator and bound to the
2341db04571cSdan ** statement.
2342db04571cSdan **
2343db04571cSdan ** An SQLite error code is returned if an error occurs. Otherwise, SQLITE_OK.
2344db04571cSdan */
23457aa469cdSdan static int sessionBindRow(
2346db04571cSdan   sqlite3_changeset_iter *pIter,  /* Iterator to read values from */
23477aa469cdSdan   int(*xValue)(sqlite3_changeset_iter *, int, sqlite3_value **),
2348db04571cSdan   int nCol,                       /* Number of columns */
2349db04571cSdan   u8 *abPK,                       /* If not NULL, bind only if true */
2350db04571cSdan   sqlite3_stmt *pStmt             /* Bind values to this statement */
2351db04571cSdan ){
2352db04571cSdan   int i;
2353db04571cSdan   int rc = SQLITE_OK;
23547aa469cdSdan 
23557aa469cdSdan   /* Neither sqlite3changeset_old or sqlite3changeset_new can fail if the
23567aa469cdSdan   ** argument iterator points to a suitable entry. Make sure that xValue
23577aa469cdSdan   ** is one of these to guarantee that it is safe to ignore the return
23587aa469cdSdan   ** in the code below. */
23597aa469cdSdan   assert( xValue==sqlite3changeset_old || xValue==sqlite3changeset_new );
23607aa469cdSdan 
2361db04571cSdan   for(i=0; rc==SQLITE_OK && i<nCol; i++){
2362db04571cSdan     if( !abPK || abPK[i] ){
2363db04571cSdan       sqlite3_value *pVal;
23647aa469cdSdan       (void)xValue(pIter, i, &pVal);
23657aa469cdSdan       rc = sessionBindValue(pStmt, i+1, pVal);
2366db04571cSdan     }
2367db04571cSdan   }
2368db04571cSdan   return rc;
2369db04571cSdan }
2370db04571cSdan 
2371db04571cSdan /*
2372296c7658Sdan ** SQL statement pSelect is as generated by the sessionSelectRow() function.
2373296c7658Sdan ** This function binds the primary key values from the change that changeset
2374296c7658Sdan ** iterator pIter points to to the SELECT and attempts to seek to the table
2375296c7658Sdan ** entry. If a row is found, the SELECT statement left pointing at the row
2376296c7658Sdan ** and SQLITE_ROW is returned. Otherwise, if no row is found and no error
2377296c7658Sdan ** has occured, the statement is reset and SQLITE_OK is returned. If an
23787aa469cdSdan ** error occurs, the statement is reset and an SQLite error code is returned.
23797aa469cdSdan **
23807aa469cdSdan ** If this function returns SQLITE_ROW, the caller must eventually reset()
23817aa469cdSdan ** statement pSelect. If any other value is returned, the statement does
23827aa469cdSdan ** not require a reset().
2383296c7658Sdan **
2384296c7658Sdan ** If the iterator currently points to an INSERT record, bind values from the
2385db04571cSdan ** new.* record to the SELECT statement. Or, if it points to a DELETE or
2386db04571cSdan ** UPDATE, bind values from the old.* record.
2387296c7658Sdan */
23880c698471Sdan static int sessionSeekToRow(
238937f133ecSdan   sqlite3 *db,                    /* Database handle */
239037f133ecSdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
239137f133ecSdan   u8 *abPK,                       /* Primary key flags array */
23920c698471Sdan   sqlite3_stmt *pSelect           /* SELECT statement from sessionSelectRow() */
239337f133ecSdan ){
23947aa469cdSdan   int rc;                         /* Return code */
2395296c7658Sdan   int nCol;                       /* Number of columns in table */
2396296c7658Sdan   int op;                         /* Changset operation (SQLITE_UPDATE etc.) */
2397296c7658Sdan   const char *zDummy;             /* Unused */
239837f133ecSdan 
2399b4480e94Sdan   sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
24007aa469cdSdan   rc = sessionBindRow(pIter,
2401db04571cSdan       op==SQLITE_INSERT ? sqlite3changeset_new : sqlite3changeset_old,
2402db04571cSdan       nCol, abPK, pSelect
2403db04571cSdan   );
24040c698471Sdan 
24050c698471Sdan   if( rc==SQLITE_OK ){
24060c698471Sdan     rc = sqlite3_step(pSelect);
24070c698471Sdan     if( rc!=SQLITE_ROW ) rc = sqlite3_reset(pSelect);
24080c698471Sdan   }
24090c698471Sdan 
24100c698471Sdan   return rc;
24110c698471Sdan }
24120c698471Sdan 
2413296c7658Sdan /*
2414296c7658Sdan ** Invoke the conflict handler for the change that the changeset iterator
2415296c7658Sdan ** currently points to.
2416296c7658Sdan **
2417296c7658Sdan ** Argument eType must be either CHANGESET_DATA or CHANGESET_CONFLICT.
2418296c7658Sdan ** If argument pbReplace is NULL, then the type of conflict handler invoked
2419296c7658Sdan ** depends solely on eType, as follows:
2420296c7658Sdan **
2421296c7658Sdan **    eType value                 Value passed to xConflict
2422296c7658Sdan **    -------------------------------------------------
2423296c7658Sdan **    CHANGESET_DATA              CHANGESET_NOTFOUND
2424296c7658Sdan **    CHANGESET_CONFLICT          CHANGESET_CONSTRAINT
2425296c7658Sdan **
2426296c7658Sdan ** Or, if pbReplace is not NULL, then an attempt is made to find an existing
2427296c7658Sdan ** record with the same primary key as the record about to be deleted, updated
2428296c7658Sdan ** or inserted. If such a record can be found, it is available to the conflict
2429296c7658Sdan ** handler as the "conflicting" record. In this case the type of conflict
2430296c7658Sdan ** handler invoked is as follows:
2431296c7658Sdan **
2432296c7658Sdan **    eType value         PK Record found?   Value passed to xConflict
2433296c7658Sdan **    ----------------------------------------------------------------
2434296c7658Sdan **    CHANGESET_DATA      Yes                CHANGESET_DATA
2435296c7658Sdan **    CHANGESET_DATA      No                 CHANGESET_NOTFOUND
2436296c7658Sdan **    CHANGESET_CONFLICT  Yes                CHANGESET_CONFLICT
2437296c7658Sdan **    CHANGESET_CONFLICT  No                 CHANGESET_CONSTRAINT
2438296c7658Sdan **
2439296c7658Sdan ** If pbReplace is not NULL, and a record with a matching PK is found, and
2440296c7658Sdan ** the conflict handler function returns SQLITE_CHANGESET_REPLACE, *pbReplace
2441296c7658Sdan ** is set to non-zero before returning SQLITE_OK.
2442296c7658Sdan **
2443296c7658Sdan ** If the conflict handler returns SQLITE_CHANGESET_ABORT, SQLITE_ABORT is
2444296c7658Sdan ** returned. Or, if the conflict handler returns an invalid value,
2445296c7658Sdan ** SQLITE_MISUSE. If the conflict handler returns SQLITE_CHANGESET_OMIT,
2446296c7658Sdan ** this function returns SQLITE_OK.
2447296c7658Sdan */
24480c698471Sdan static int sessionConflictHandler(
2449296c7658Sdan   int eType,                      /* Either CHANGESET_DATA or CONFLICT */
2450296c7658Sdan   SessionApplyCtx *p,             /* changeset_apply() context */
24510c698471Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
24520c698471Sdan   int(*xConflict)(void *, int, sqlite3_changeset_iter*),
2453296c7658Sdan   void *pCtx,                     /* First argument for conflict handler */
2454296c7658Sdan   int *pbReplace                  /* OUT: Set to true if PK row is found */
24550c698471Sdan ){
2456296c7658Sdan   int res;                        /* Value returned by conflict handler */
24570c698471Sdan   int rc;
24580c698471Sdan   int nCol;
24590c698471Sdan   int op;
24600c698471Sdan   const char *zDummy;
24610c698471Sdan 
2462b4480e94Sdan   sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
24630c698471Sdan 
24640c698471Sdan   assert( eType==SQLITE_CHANGESET_CONFLICT || eType==SQLITE_CHANGESET_DATA );
24650c698471Sdan   assert( SQLITE_CHANGESET_CONFLICT+1==SQLITE_CHANGESET_CONSTRAINT );
24660c698471Sdan   assert( SQLITE_CHANGESET_DATA+1==SQLITE_CHANGESET_NOTFOUND );
246737f133ecSdan 
246837f133ecSdan   /* Bind the new.* PRIMARY KEY values to the SELECT statement. */
24690c698471Sdan   if( pbReplace ){
24700c698471Sdan     rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect);
24710c698471Sdan   }else{
2472db04571cSdan     rc = SQLITE_OK;
24730c698471Sdan   }
24740c698471Sdan 
24750c698471Sdan   if( rc==SQLITE_ROW ){
24760c698471Sdan     /* There exists another row with the new.* primary key. */
24770c698471Sdan     pIter->pConflict = p->pSelect;
24780c698471Sdan     res = xConflict(pCtx, eType, pIter);
24790c698471Sdan     pIter->pConflict = 0;
24800c698471Sdan     rc = sqlite3_reset(p->pSelect);
2481db04571cSdan   }else if( rc==SQLITE_OK ){
24820c698471Sdan     /* No other row with the new.* primary key. */
24830c698471Sdan     res = xConflict(pCtx, eType+1, pIter);
24840c698471Sdan     if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE;
248537f133ecSdan   }
248637f133ecSdan 
248737f133ecSdan   if( rc==SQLITE_OK ){
24880c698471Sdan     switch( res ){
24890c698471Sdan       case SQLITE_CHANGESET_REPLACE:
2490f51e5f6cSdan         assert( pbReplace );
2491f51e5f6cSdan         *pbReplace = 1;
24920c698471Sdan         break;
24930c698471Sdan 
24940c698471Sdan       case SQLITE_CHANGESET_OMIT:
24950c698471Sdan         break;
24960c698471Sdan 
24970c698471Sdan       case SQLITE_CHANGESET_ABORT:
24980c698471Sdan         rc = SQLITE_ABORT;
24990c698471Sdan         break;
25000c698471Sdan 
25010c698471Sdan       default:
25020c698471Sdan         rc = SQLITE_MISUSE;
25030c698471Sdan         break;
25040c698471Sdan     }
25050c698471Sdan   }
25060c698471Sdan 
25070c698471Sdan   return rc;
25080c698471Sdan }
25090c698471Sdan 
2510296c7658Sdan /*
2511296c7658Sdan ** Attempt to apply the change that the iterator passed as the first argument
2512296c7658Sdan ** currently points to to the database. If a conflict is encountered, invoke
2513296c7658Sdan ** the conflict handler callback.
2514296c7658Sdan **
2515296c7658Sdan ** If argument pbRetry is NULL, then ignore any CHANGESET_DATA conflict. If
2516296c7658Sdan ** one is encountered, update or delete the row with the matching primary key
2517296c7658Sdan ** instead. Or, if pbRetry is not NULL and a CHANGESET_DATA conflict occurs,
2518296c7658Sdan ** invoke the conflict handler. If it returns CHANGESET_REPLACE, set *pbRetry
2519296c7658Sdan ** to true before returning. In this case the caller will invoke this function
2520296c7658Sdan ** again, this time with pbRetry set to NULL.
2521296c7658Sdan **
2522296c7658Sdan ** If argument pbReplace is NULL and a CHANGESET_CONFLICT conflict is
2523296c7658Sdan ** encountered invoke the conflict handler with CHANGESET_CONSTRAINT instead.
2524296c7658Sdan ** Or, if pbReplace is not NULL, invoke it with CHANGESET_CONFLICT. If such
2525296c7658Sdan ** an invocation returns SQLITE_CHANGESET_REPLACE, set *pbReplace to true
2526296c7658Sdan ** before retrying. In this case the caller attempts to remove the conflicting
2527296c7658Sdan ** row before invoking this function again, this time with pbReplace set
2528296c7658Sdan ** to NULL.
2529296c7658Sdan **
2530296c7658Sdan ** If any conflict handler returns SQLITE_CHANGESET_ABORT, this function
2531296c7658Sdan ** returns SQLITE_ABORT. Otherwise, if no error occurs, SQLITE_OK is
2532296c7658Sdan ** returned.
2533296c7658Sdan */
25340c698471Sdan static int sessionApplyOneOp(
2535296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
2536296c7658Sdan   SessionApplyCtx *p,             /* changeset_apply() context */
25370c698471Sdan   int(*xConflict)(void *, int, sqlite3_changeset_iter *),
2538296c7658Sdan   void *pCtx,                     /* First argument for the conflict handler */
2539296c7658Sdan   int *pbReplace,                 /* OUT: True to remove PK row and retry */
2540296c7658Sdan   int *pbRetry                    /* OUT: True to retry. */
25410c698471Sdan ){
25420c698471Sdan   const char *zDummy;
25430c698471Sdan   int op;
25440c698471Sdan   int nCol;
25450c698471Sdan   int rc = SQLITE_OK;
25460c698471Sdan 
25470c698471Sdan   assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect );
25480c698471Sdan   assert( p->azCol && p->abPK );
25490c698471Sdan   assert( !pbReplace || *pbReplace==0 );
25500c698471Sdan 
2551b4480e94Sdan   sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
25520c698471Sdan 
25530c698471Sdan   if( op==SQLITE_DELETE ){
25540c698471Sdan 
25550c698471Sdan     /* Bind values to the DELETE statement. */
25567aa469cdSdan     rc = sessionBindRow(pIter, sqlite3changeset_old, nCol, 0, p->pDelete);
25577cf7df7dSdan     if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){
25587cf7df7dSdan       rc = sqlite3_bind_int(p->pDelete, nCol+1, pbRetry==0);
25597cf7df7dSdan     }
25600c698471Sdan     if( rc!=SQLITE_OK ) return rc;
25610c698471Sdan 
25620c698471Sdan     sqlite3_step(p->pDelete);
25630c698471Sdan     rc = sqlite3_reset(p->pDelete);
25640c698471Sdan     if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
25650c698471Sdan       rc = sessionConflictHandler(
25660c698471Sdan           SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
25670c698471Sdan       );
25680c698471Sdan     }else if( rc==SQLITE_CONSTRAINT ){
25690c698471Sdan       rc = sessionConflictHandler(
25700c698471Sdan           SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
25710c698471Sdan       );
25720c698471Sdan     }
25730c698471Sdan 
25740c698471Sdan   }else if( op==SQLITE_UPDATE ){
25750c698471Sdan     int i;
25760c698471Sdan 
25770c698471Sdan     /* Bind values to the UPDATE statement. */
25780c698471Sdan     for(i=0; rc==SQLITE_OK && i<nCol; i++){
25797aa469cdSdan       sqlite3_value *pOld = sessionChangesetOld(pIter, i);
25807aa469cdSdan       sqlite3_value *pNew = sessionChangesetNew(pIter, i);
25817aa469cdSdan 
25820c698471Sdan       sqlite3_bind_int(p->pUpdate, i*3+2, !!pNew);
25837aa469cdSdan       if( pOld ){
25847aa469cdSdan         rc = sessionBindValue(p->pUpdate, i*3+1, pOld);
25857aa469cdSdan       }
25867aa469cdSdan       if( rc==SQLITE_OK && pNew ){
25877aa469cdSdan         rc = sessionBindValue(p->pUpdate, i*3+3, pNew);
25880c698471Sdan       }
25890c698471Sdan     }
25907aa469cdSdan     if( rc==SQLITE_OK ) sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0);
25910c698471Sdan     if( rc!=SQLITE_OK ) return rc;
25920c698471Sdan 
25930c698471Sdan     /* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict,
25940c698471Sdan     ** the result will be SQLITE_OK with 0 rows modified. */
25950c698471Sdan     sqlite3_step(p->pUpdate);
25960c698471Sdan     rc = sqlite3_reset(p->pUpdate);
25970c698471Sdan 
25980c698471Sdan     if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
25990c698471Sdan       /* A NOTFOUND or DATA error. Search the table to see if it contains
26000c698471Sdan       ** a row with a matching primary key. If so, this is a DATA conflict.
26010c698471Sdan       ** Otherwise, if there is no primary key match, it is a NOTFOUND. */
26020c698471Sdan 
26030c698471Sdan       rc = sessionConflictHandler(
26040c698471Sdan           SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
26050c698471Sdan       );
26060c698471Sdan 
26070c698471Sdan     }else if( rc==SQLITE_CONSTRAINT ){
2608db04571cSdan       /* This is always a CONSTRAINT conflict. */
2609db04571cSdan       rc = sessionConflictHandler(
2610db04571cSdan           SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
26110c698471Sdan       );
26120c698471Sdan     }
26130c698471Sdan 
26140c698471Sdan   }else{
26150c698471Sdan     assert( op==SQLITE_INSERT );
26167aa469cdSdan     rc = sessionBindRow(pIter, sqlite3changeset_new, nCol, 0, p->pInsert);
26170c698471Sdan     if( rc!=SQLITE_OK ) return rc;
26180c698471Sdan 
26190c698471Sdan     sqlite3_step(p->pInsert);
26200c698471Sdan     rc = sqlite3_reset(p->pInsert);
2621db04571cSdan     if( rc==SQLITE_CONSTRAINT ){
26220c698471Sdan       rc = sessionConflictHandler(
26230c698471Sdan           SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, pbReplace
26240c698471Sdan       );
262537f133ecSdan     }
262637f133ecSdan   }
262737f133ecSdan 
262837f133ecSdan   return rc;
262937f133ecSdan }
263037f133ecSdan 
2631296c7658Sdan /*
2632296c7658Sdan ** Apply the changeset passed via pChangeset/nChangeset to the main database
2633296c7658Sdan ** attached to handle "db". Invoke the supplied conflict handler callback
2634296c7658Sdan ** to resolve any conflicts encountered while applying the change.
2635296c7658Sdan */
2636d5f0767cSdan int sqlite3changeset_apply(
2637296c7658Sdan   sqlite3 *db,                    /* Apply change to "main" db of this handle */
2638296c7658Sdan   int nChangeset,                 /* Size of changeset in bytes */
2639296c7658Sdan   void *pChangeset,               /* Changeset blob */
2640d5f0767cSdan   int(*xConflict)(
2641d5f0767cSdan     void *pCtx,                   /* Copy of fifth arg to _apply() */
2642d5f0767cSdan     int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
2643d5f0767cSdan     sqlite3_changeset_iter *p     /* Handle describing change and conflict */
2644d5f0767cSdan   ),
2645296c7658Sdan   void *pCtx                      /* First argument passed to xConflict */
2646d5f0767cSdan ){
2647ca62ad57Sdan   int schemaMismatch = 0;
2648296c7658Sdan   sqlite3_changeset_iter *pIter;  /* Iterator to skip through changeset */
2649296c7658Sdan   int rc;                         /* Return code */
2650d5f0767cSdan   const char *zTab = 0;           /* Name of current table */
2651cfdbde21Sdrh   int nTab = 0;                   /* Result of sqlite3Strlen30(zTab) */
2652296c7658Sdan   SessionApplyCtx sApply;         /* changeset_apply() context object */
2653d5f0767cSdan 
26540c698471Sdan   memset(&sApply, 0, sizeof(sApply));
265512ca0b56Sdan   rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
265612ca0b56Sdan   if( rc!=SQLITE_OK ) return rc;
26570c698471Sdan 
26584c220252Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
26590c698471Sdan   rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
26600c698471Sdan   while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter) ){
26610c698471Sdan     int nCol;
2662d5f0767cSdan     int op;
26630c698471Sdan     int bReplace = 0;
26640c698471Sdan     int bRetry = 0;
26650c698471Sdan     const char *zNew;
2666ca62ad57Sdan 
2667b4480e94Sdan     sqlite3changeset_op(pIter, &zNew, &nCol, &op, 0);
2668d5f0767cSdan 
26690c698471Sdan     if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){
2670ca62ad57Sdan       u8 *abPK;
2671ca62ad57Sdan 
2672ca62ad57Sdan       schemaMismatch = 0;
2673cfdbde21Sdrh       sqlite3_free((char*)sApply.azCol);  /* cast works around VC++ bug */
26740c698471Sdan       sqlite3_finalize(sApply.pDelete);
26750c698471Sdan       sqlite3_finalize(sApply.pUpdate);
26760c698471Sdan       sqlite3_finalize(sApply.pInsert);
26770c698471Sdan       sqlite3_finalize(sApply.pSelect);
26780c698471Sdan       memset(&sApply, 0, sizeof(sApply));
26790c698471Sdan       sApply.db = db;
268037f133ecSdan 
2681ca62ad57Sdan       sqlite3changeset_pk(pIter, &abPK, 0);
2682296c7658Sdan       rc = sessionTableInfo(
2683ca62ad57Sdan           db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
2684ca62ad57Sdan       );
2685ca62ad57Sdan       if( rc!=SQLITE_OK ) break;
26860c698471Sdan 
2687ca62ad57Sdan       if( sApply.nCol==0 ){
2688ca62ad57Sdan         schemaMismatch = 1;
2689ca62ad57Sdan         sqlite3_log(SQLITE_SCHEMA,
2690ca62ad57Sdan             "sqlite3changeset_apply(): no such table: %s", zTab
2691ca62ad57Sdan         );
2692ca62ad57Sdan       }
2693ca62ad57Sdan       else if( sApply.nCol!=nCol ){
2694ca62ad57Sdan         schemaMismatch = 1;
2695ca62ad57Sdan         sqlite3_log(SQLITE_SCHEMA,
2696ca62ad57Sdan             "sqlite3changeset_apply(): table %s has %d columns, expected %d",
2697ca62ad57Sdan             zTab, sApply.nCol, nCol
2698ca62ad57Sdan         );
2699ca62ad57Sdan       }
2700ca62ad57Sdan       else if( memcmp(sApply.abPK, abPK, nCol)!=0 ){
2701ca62ad57Sdan         schemaMismatch = 1;
2702ca62ad57Sdan         sqlite3_log(SQLITE_SCHEMA,
2703ca62ad57Sdan             "sqlite3changeset_apply(): primary key mismatch for table %s", zTab
2704ca62ad57Sdan         );
2705ca62ad57Sdan       }
2706ca62ad57Sdan       else if(
2707ca62ad57Sdan           (rc = sessionSelectRow(db, zTab, &sApply))
27080c698471Sdan        || (rc = sessionUpdateRow(db, zTab, &sApply))
27090c698471Sdan        || (rc = sessionDeleteRow(db, zTab, &sApply))
27100c698471Sdan        || (rc = sessionInsertRow(db, zTab, &sApply))
271137f133ecSdan       ){
271237f133ecSdan         break;
271337f133ecSdan       }
2714cfdbde21Sdrh       nTab = sqlite3Strlen30(zTab);
2715d5f0767cSdan     }
2716d5f0767cSdan 
2717ca62ad57Sdan     /* If there is a schema mismatch on the current table, proceed to the
2718ca62ad57Sdan     ** next change. A log message has already been issued. */
2719ca62ad57Sdan     if( schemaMismatch ) continue;
2720ca62ad57Sdan 
27210c698471Sdan     rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, &bReplace, &bRetry);
27220c698471Sdan 
27230c698471Sdan     if( rc==SQLITE_OK && bRetry ){
27240c698471Sdan       rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, &bReplace, 0);
27250c698471Sdan     }
27260c698471Sdan 
27270c698471Sdan     if( bReplace ){
2728db04571cSdan       assert( pIter->op==SQLITE_INSERT );
27290c698471Sdan       rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
27300c698471Sdan       if( rc==SQLITE_OK ){
27317aa469cdSdan         rc = sessionBindRow(pIter,
2732db04571cSdan             sqlite3changeset_new, sApply.nCol, sApply.abPK, sApply.pDelete);
27330c698471Sdan         sqlite3_bind_int(sApply.pDelete, sApply.nCol+1, 1);
27340c698471Sdan       }
27350c698471Sdan       if( rc==SQLITE_OK ){
27360c698471Sdan         sqlite3_step(sApply.pDelete);
27370c698471Sdan         rc = sqlite3_reset(sApply.pDelete);
27380c698471Sdan       }
27390c698471Sdan       if( rc==SQLITE_OK ){
27400c698471Sdan         rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, 0, 0);
27410c698471Sdan       }
27420c698471Sdan       if( rc==SQLITE_OK ){
27430c698471Sdan         rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0);
27440c698471Sdan       }
27450c698471Sdan     }
27460c698471Sdan   }
2747d5f0767cSdan 
2748296c7658Sdan   if( rc==SQLITE_OK ){
2749296c7658Sdan     rc = sqlite3changeset_finalize(pIter);
2750296c7658Sdan   }else{
2751296c7658Sdan     sqlite3changeset_finalize(pIter);
2752296c7658Sdan   }
2753d5f0767cSdan 
2754d5f0767cSdan   if( rc==SQLITE_OK ){
2755d5f0767cSdan     rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
2756d5f0767cSdan   }else{
2757d5f0767cSdan     sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
2758d5f0767cSdan     sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
2759d5f0767cSdan   }
2760d5f0767cSdan 
27610c698471Sdan   sqlite3_finalize(sApply.pInsert);
27620c698471Sdan   sqlite3_finalize(sApply.pDelete);
27630c698471Sdan   sqlite3_finalize(sApply.pUpdate);
27640c698471Sdan   sqlite3_finalize(sApply.pSelect);
2765cfdbde21Sdrh   sqlite3_free((char*)sApply.azCol);  /* cast works around VC++ bug */
27664c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
2767d5f0767cSdan   return rc;
2768d5f0767cSdan }
276991ddd559Sdan 
27705d607a6eSdan static int sessionChangeMerge(
27715d607a6eSdan   SessionTable *pTab,
27725d607a6eSdan   SessionChange *pExist,
27735d607a6eSdan   int op2,
27745d607a6eSdan   int bIndirect,
27755d607a6eSdan   u8 *aRec,
27765d607a6eSdan   int nRec,
27775d607a6eSdan   SessionChange **ppNew
27785d607a6eSdan ){
27795d607a6eSdan   SessionChange *pNew = 0;
27805d607a6eSdan 
27815d607a6eSdan   if( !pExist ){
27825d607a6eSdan     pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange));
27835d607a6eSdan     if( !pNew ){
27845d607a6eSdan       return SQLITE_NOMEM;
27855d607a6eSdan     }
27865d607a6eSdan     memset(pNew, 0, sizeof(SessionChange));
27875d607a6eSdan     pNew->bInsert = op2;
27885d607a6eSdan     pNew->bIndirect = bIndirect;
27895d607a6eSdan     pNew->nRecord = nRec;
27905d607a6eSdan     pNew->aRecord = aRec;
27915d607a6eSdan   }else{
27925d607a6eSdan     int op1 = pExist->bInsert;
27935d607a6eSdan 
27945d607a6eSdan     /*
27955d607a6eSdan     **   op1=INSERT, op2=INSERT      ->      Unsupported. Discard op2.
27965d607a6eSdan     **   op1=INSERT, op2=UPDATE      ->      INSERT.
27975d607a6eSdan     **   op1=INSERT, op2=DELETE      ->      (none)
27985d607a6eSdan     **
27995d607a6eSdan     **   op1=UPDATE, op2=INSERT      ->      Unsupported. Discard op2.
28005d607a6eSdan     **   op1=UPDATE, op2=UPDATE      ->      UPDATE.
28015d607a6eSdan     **   op1=UPDATE, op2=DELETE      ->      DELETE.
28025d607a6eSdan     **
28035d607a6eSdan     **   op1=DELETE, op2=INSERT      ->      UPDATE.
28045d607a6eSdan     **   op1=DELETE, op2=UPDATE      ->      Unsupported. Discard op2.
28055d607a6eSdan     **   op1=DELETE, op2=DELETE      ->      Unsupported. Discard op2.
28065d607a6eSdan     */
28075d607a6eSdan     if( (op1==SQLITE_INSERT && op2==SQLITE_INSERT)
28085d607a6eSdan      || (op1==SQLITE_UPDATE && op2==SQLITE_INSERT)
28095d607a6eSdan      || (op1==SQLITE_DELETE && op2==SQLITE_UPDATE)
28105d607a6eSdan      || (op1==SQLITE_DELETE && op2==SQLITE_DELETE)
28115d607a6eSdan     ){
28125d607a6eSdan       pNew = pExist;
28135d607a6eSdan     }else if( op1==SQLITE_INSERT && op2==SQLITE_DELETE ){
28145d607a6eSdan       sqlite3_free(pExist);
28155d607a6eSdan       assert( pNew==0 );
28165d607a6eSdan     }else{
28175d607a6eSdan       int nByte;
28185d607a6eSdan       u8 *aCsr;
28195d607a6eSdan 
28205d607a6eSdan       nByte = sizeof(SessionChange) + pExist->nRecord + nRec;
28215d607a6eSdan       pNew = (SessionChange *)sqlite3_malloc(nByte);
28225d607a6eSdan       if( !pNew ){
28231756ae10Sdan         sqlite3_free(pExist);
28245d607a6eSdan         return SQLITE_NOMEM;
28255d607a6eSdan       }
28265d607a6eSdan       memset(pNew, 0, sizeof(SessionChange));
28275d607a6eSdan       pNew->bIndirect = (bIndirect && pExist->bIndirect);
28285d607a6eSdan       aCsr = pNew->aRecord = (u8 *)&pNew[1];
28295d607a6eSdan 
2830b08a1efaSdan       if( op1==SQLITE_INSERT ){             /* INSERT + UPDATE */
28315d607a6eSdan         u8 *a1 = aRec;
2832b08a1efaSdan         assert( op2==SQLITE_UPDATE );
28335d607a6eSdan         pNew->bInsert = SQLITE_INSERT;
28345d607a6eSdan         sessionReadRecord(&a1, pTab->nCol, 0);
28355d607a6eSdan         sessionMergeRecord(&aCsr, pTab, pExist->aRecord, a1);
2836b08a1efaSdan       }else if( op1==SQLITE_DELETE ){       /* DELETE + INSERT */
2837b08a1efaSdan         assert( op2==SQLITE_INSERT );
2838b08a1efaSdan         pNew->bInsert = SQLITE_UPDATE;
2839b08a1efaSdan         if( 0==sessionMergeUpdate(&aCsr, pTab, pExist->aRecord, 0, aRec, 0) ){
2840b08a1efaSdan           sqlite3_free(pNew);
2841b08a1efaSdan           pNew = 0;
28425d607a6eSdan         }
2843b08a1efaSdan       }else if( op2==SQLITE_UPDATE ){       /* UPDATE + UPDATE */
2844b08a1efaSdan         assert( op1==SQLITE_UPDATE );
28455d607a6eSdan         u8 *a1 = pExist->aRecord;
28465d607a6eSdan         u8 *a2 = aRec;
28475d607a6eSdan         sessionReadRecord(&a1, pTab->nCol, 0);
28485d607a6eSdan         sessionReadRecord(&a2, pTab->nCol, 0);
28495d607a6eSdan         pNew->bInsert = SQLITE_UPDATE;
28505d607a6eSdan         if( 0==sessionMergeUpdate(&aCsr, pTab, aRec, pExist->aRecord, a1, a2) ){
28515d607a6eSdan           sqlite3_free(pNew);
28525d607a6eSdan           pNew = 0;
28535d607a6eSdan         }
2854b08a1efaSdan       }else{                                /* UPDATE + DELETE */
2855b08a1efaSdan         assert( op1==SQLITE_UPDATE && op2==SQLITE_DELETE );
28565d607a6eSdan         pNew->bInsert = SQLITE_DELETE;
28575d607a6eSdan         sessionMergeRecord(&aCsr, pTab, aRec, pExist->aRecord);
28585d607a6eSdan       }
28595d607a6eSdan 
28605d607a6eSdan       if( pNew ){
28615d607a6eSdan         pNew->nRecord = (aCsr - pNew->aRecord);
28625d607a6eSdan       }
28635d607a6eSdan       sqlite3_free(pExist);
28645d607a6eSdan     }
28655d607a6eSdan   }
28665d607a6eSdan 
28675d607a6eSdan   *ppNew = pNew;
28685d607a6eSdan   return SQLITE_OK;
28695d607a6eSdan }
28705d607a6eSdan 
28715d607a6eSdan int sessionConcatChangeset(
28725d607a6eSdan   int nChangeset,
28735d607a6eSdan   void *pChangeset,
28745d607a6eSdan   SessionTable **ppTabList
28755d607a6eSdan ){
28765d607a6eSdan   u8 *aRec;
28775d607a6eSdan   int nRec;
28785d607a6eSdan   sqlite3_changeset_iter *pIter;
28795d607a6eSdan   int rc;
28805d607a6eSdan   SessionTable *pTab = 0;
28815d607a6eSdan 
28825d607a6eSdan   rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
28835d607a6eSdan   if( rc!=SQLITE_OK ) return rc;
28845d607a6eSdan 
28855d607a6eSdan   while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec) ){
28865d607a6eSdan     const char *zNew;
28875d607a6eSdan     int nCol;
28885d607a6eSdan     int op;
28895d607a6eSdan     int iHash;
28905d607a6eSdan     int bIndirect;
28915d607a6eSdan     SessionChange *pChange;
28925d607a6eSdan     SessionChange *pExist = 0;
28935d607a6eSdan     SessionChange **pp;
28945d607a6eSdan 
28955d607a6eSdan     assert( pIter->apValue==0 );
28965d607a6eSdan     sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect);
28975d607a6eSdan 
2898b08a1efaSdan     assert( zNew>=(char *)pChangeset && zNew-nChangeset<((char *)pChangeset) );
2899b08a1efaSdan     assert( !pTab || pTab->zName-nChangeset<(char *)pChangeset );
2900b08a1efaSdan     assert( !pTab || zNew>=pTab->zName );
2901b08a1efaSdan 
29025d607a6eSdan     if( !pTab || zNew!=pTab->zName ){
29035d607a6eSdan       /* Search the list for a matching table */
29045d607a6eSdan       int nNew = strlen(zNew);
2905f29123b5Sdan       u8 *abPK;
2906f29123b5Sdan 
2907f29123b5Sdan       sqlite3changeset_pk(pIter, &abPK, 0);
29085d607a6eSdan       for(pTab = *ppTabList; pTab; pTab=pTab->pNext){
29095d607a6eSdan         if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break;
29105d607a6eSdan       }
29115d607a6eSdan       if( !pTab ){
29125d607a6eSdan         pTab = sqlite3_malloc(sizeof(SessionTable));
2913f29123b5Sdan         if( !pTab ){
2914f29123b5Sdan           rc = SQLITE_NOMEM;
2915f29123b5Sdan           break;
2916f29123b5Sdan         }
29175d607a6eSdan         memset(pTab, 0, sizeof(SessionTable));
29185d607a6eSdan         pTab->pNext = *ppTabList;
2919f29123b5Sdan         pTab->abPK = abPK;
2920f29123b5Sdan         pTab->nCol = nCol;
29215d607a6eSdan         *ppTabList = pTab;
2922f29123b5Sdan       }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){
2923f29123b5Sdan         rc = SQLITE_SCHEMA;
2924f29123b5Sdan         break;
29255d607a6eSdan       }
29265d607a6eSdan       pTab->zName = (char *)zNew;
29275d607a6eSdan     }
29285d607a6eSdan 
29291756ae10Sdan     if( sessionGrowHash(pTab) ){
29301756ae10Sdan       rc = SQLITE_NOMEM;
29311756ae10Sdan       break;
29321756ae10Sdan     }
29335d607a6eSdan     iHash = sessionChangeHash(pTab, aRec, pTab->nChange);
29345d607a6eSdan 
29355d607a6eSdan     /* Search for existing entry. If found, remove it from the hash table.
29365d607a6eSdan     ** Code below may link it back in.
29375d607a6eSdan     */
29385d607a6eSdan     for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
29395d607a6eSdan       if( sessionChangeEqual(pTab, (*pp)->aRecord, aRec) ){
29405d607a6eSdan         pExist = *pp;
29415d607a6eSdan         *pp = (*pp)->pNext;
29425d607a6eSdan         pTab->nEntry--;
29435d607a6eSdan         break;
29445d607a6eSdan       }
29455d607a6eSdan     }
29465d607a6eSdan 
29475d607a6eSdan     rc = sessionChangeMerge(pTab, pExist, op, bIndirect, aRec, nRec, &pChange);
29485d607a6eSdan     if( rc ) break;
29495d607a6eSdan     if( pChange ){
29505d607a6eSdan       pChange->pNext = pTab->apChange[iHash];
29515d607a6eSdan       pTab->apChange[iHash] = pChange;
29525d607a6eSdan       pTab->nEntry++;
29535d607a6eSdan     }
29545d607a6eSdan   }
29555d607a6eSdan 
29565d607a6eSdan   if( rc==SQLITE_OK ){
29575d607a6eSdan     rc = sqlite3changeset_finalize(pIter);
29585d607a6eSdan   }else{
29595d607a6eSdan     sqlite3changeset_finalize(pIter);
29605d607a6eSdan   }
29615d607a6eSdan   return rc;
29625d607a6eSdan }
29635d607a6eSdan 
29645d607a6eSdan 
29655d607a6eSdan /*
29665d607a6eSdan ** 1. Iterate through the left-hand changeset. Add an entry to a table
29675d607a6eSdan **    specific hash table for each change in the changeset. The hash table
29685d607a6eSdan **    key is the PK of the row affected by the change.
29695d607a6eSdan **
29705d607a6eSdan ** 2. Then interate through the right-hand changeset. Attempt to add an
29715d607a6eSdan **    entry to a hash table for each component change. If a change already
29725d607a6eSdan **    exists with the same PK values, combine the two into a single change.
29735d607a6eSdan **
29745d607a6eSdan ** 3. Write an output changeset based on the contents of the hash table.
29755d607a6eSdan */
29765d607a6eSdan int sqlite3changeset_concat(
29775d607a6eSdan   int nLeft,                      /* Number of bytes in lhs input */
29785d607a6eSdan   void *pLeft,                    /* Lhs input changeset */
29795d607a6eSdan   int nRight                      /* Number of bytes in rhs input */,
29805d607a6eSdan   void *pRight,                   /* Rhs input changeset */
29815d607a6eSdan   int *pnOut,                     /* OUT: Number of bytes in output changeset */
29825d607a6eSdan   void **ppOut                    /* OUT: changeset (left <concat> right) */
29835d607a6eSdan ){
29845d607a6eSdan   SessionTable *pList = 0;        /* List of SessionTable objects */
29855d607a6eSdan   int rc;                         /* Return code */
29865d607a6eSdan 
29875d607a6eSdan   *pnOut = 0;
29885d607a6eSdan   *ppOut = 0;
29895d607a6eSdan 
29905d607a6eSdan   rc = sessionConcatChangeset(nLeft, pLeft, &pList);
29915d607a6eSdan   if( rc==SQLITE_OK ){
29925d607a6eSdan     rc = sessionConcatChangeset(nRight, pRight, &pList);
29935d607a6eSdan   }
29945d607a6eSdan 
29955d607a6eSdan   /* Create the serialized output changeset based on the contents of the
29965d607a6eSdan   ** hash tables attached to the SessionTable objects in list pList.
29975d607a6eSdan   */
29985d607a6eSdan   if( rc==SQLITE_OK ){
29995d607a6eSdan     SessionTable *pTab;
30005d607a6eSdan     SessionBuffer buf = {0, 0, 0};
30015d607a6eSdan     for(pTab=pList; pTab; pTab=pTab->pNext){
30025d607a6eSdan       int i;
30035d607a6eSdan       if( pTab->nEntry==0 ) continue;
30045d607a6eSdan 
30055d607a6eSdan       sessionAppendTableHdr(&buf, pTab, &rc);
30065d607a6eSdan       for(i=0; i<pTab->nChange; i++){
30075d607a6eSdan         SessionChange *p;
30085d607a6eSdan         for(p=pTab->apChange[i]; p; p=p->pNext){
30095d607a6eSdan           sessionAppendByte(&buf, p->bInsert, &rc);
30105d607a6eSdan           sessionAppendByte(&buf, p->bIndirect, &rc);
30115d607a6eSdan           sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
30125d607a6eSdan         }
30135d607a6eSdan       }
30145d607a6eSdan     }
30155d607a6eSdan 
30165d607a6eSdan     if( rc==SQLITE_OK ){
30175d607a6eSdan       *ppOut = buf.aBuf;
30185d607a6eSdan       *pnOut = buf.nBuf;
30195d607a6eSdan     }else{
30205d607a6eSdan       sqlite3_free(buf.aBuf);
30215d607a6eSdan     }
30225d607a6eSdan   }
30235d607a6eSdan 
30245d607a6eSdan   sessionDeleteTable(pList);
30255d607a6eSdan   return rc;
30265d607a6eSdan }
30275d607a6eSdan 
30289b1c62d4Sdrh #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
3029