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.
1294fccf43aSdan **   old.* record: (delete and update only)
1304fccf43aSdan **   new.* record: (insert and update only)
1314fccf43aSdan */
1324fccf43aSdan 
1334fccf43aSdan /*
1344fccf43aSdan ** For each row modified during a session, there exists a single instance of
1354fccf43aSdan ** this structure stored in a SessionTable.aChange[] hash table.
1364fccf43aSdan */
1374fccf43aSdan struct SessionChange {
138e8d5648eSdan   int bInsert;                    /* True if row was inserted this session */
139b4480e94Sdan   int bIndirect;                  /* True if this change is "indirect" */
1404fccf43aSdan   int nRecord;                    /* Number of bytes in buffer aRecord[] */
1414fccf43aSdan   u8 *aRecord;                    /* Buffer containing old.* record */
1424fccf43aSdan   SessionChange *pNext;           /* For hash-table collisions */
1434fccf43aSdan };
1444fccf43aSdan 
145296c7658Sdan /*
146296c7658Sdan ** Instances of this structure are used to build strings or binary records.
147296c7658Sdan */
148296c7658Sdan struct SessionBuffer {
149296c7658Sdan   u8 *aBuf;                       /* Pointer to changeset buffer */
150296c7658Sdan   int nBuf;                       /* Size of buffer aBuf */
151296c7658Sdan   int nAlloc;                     /* Size of allocation containing aBuf */
152296c7658Sdan };
1534fccf43aSdan 
154296c7658Sdan /*
155296c7658Sdan ** Write a varint with value iVal into the buffer at aBuf. Return the
156296c7658Sdan ** number of bytes written.
157296c7658Sdan */
158296c7658Sdan static int sessionVarintPut(u8 *aBuf, int iVal){
159296c7658Sdan   return putVarint32(aBuf, iVal);
1604fccf43aSdan }
1614fccf43aSdan 
162296c7658Sdan /*
163296c7658Sdan ** Return the number of bytes required to store value iVal as a varint.
164296c7658Sdan */
165296c7658Sdan static int sessionVarintLen(int iVal){
166296c7658Sdan   return sqlite3VarintLen(iVal);
167296c7658Sdan }
168296c7658Sdan 
169296c7658Sdan /*
170296c7658Sdan ** Read a varint value from aBuf[] into *piVal. Return the number of
171296c7658Sdan ** bytes read.
172296c7658Sdan */
1734fccf43aSdan static int sessionVarintGet(u8 *aBuf, int *piVal){
174296c7658Sdan   return getVarint32(aBuf, *piVal);
1754fccf43aSdan }
1764fccf43aSdan 
177296c7658Sdan /*
178296c7658Sdan ** Read a 64-bit big-endian integer value from buffer aRec[]. Return
179296c7658Sdan ** the value read.
180296c7658Sdan */
1814fccf43aSdan static sqlite3_int64 sessionGetI64(u8 *aRec){
1824fccf43aSdan   return (((sqlite3_int64)aRec[0]) << 56)
1834fccf43aSdan        + (((sqlite3_int64)aRec[1]) << 48)
1844fccf43aSdan        + (((sqlite3_int64)aRec[2]) << 40)
1854fccf43aSdan        + (((sqlite3_int64)aRec[3]) << 32)
1864fccf43aSdan        + (((sqlite3_int64)aRec[4]) << 24)
1874fccf43aSdan        + (((sqlite3_int64)aRec[5]) << 16)
1884fccf43aSdan        + (((sqlite3_int64)aRec[6]) <<  8)
1894fccf43aSdan        + (((sqlite3_int64)aRec[7]) <<  0);
1904fccf43aSdan }
1914fccf43aSdan 
1924fccf43aSdan /*
193296c7658Sdan ** Write a 64-bit big-endian integer value to the buffer aBuf[].
194296c7658Sdan */
195296c7658Sdan static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){
196296c7658Sdan   aBuf[0] = (i>>56) & 0xFF;
197296c7658Sdan   aBuf[1] = (i>>48) & 0xFF;
198296c7658Sdan   aBuf[2] = (i>>40) & 0xFF;
199296c7658Sdan   aBuf[3] = (i>>32) & 0xFF;
200296c7658Sdan   aBuf[4] = (i>>24) & 0xFF;
201296c7658Sdan   aBuf[5] = (i>>16) & 0xFF;
202296c7658Sdan   aBuf[6] = (i>> 8) & 0xFF;
203296c7658Sdan   aBuf[7] = (i>> 0) & 0xFF;
204296c7658Sdan }
205296c7658Sdan 
206296c7658Sdan /*
2074fccf43aSdan ** This function is used to serialize the contents of value pValue (see
2084fccf43aSdan ** comment titled "RECORD FORMAT" above).
2094fccf43aSdan **
2104fccf43aSdan ** If it is non-NULL, the serialized form of the value is written to
2114fccf43aSdan ** buffer aBuf. *pnWrite is set to the number of bytes written before
2124fccf43aSdan ** returning. Or, if aBuf is NULL, the only thing this function does is
2134fccf43aSdan ** set *pnWrite.
2144fccf43aSdan **
2154fccf43aSdan ** If no error occurs, SQLITE_OK is returned. Or, if an OOM error occurs
2164fccf43aSdan ** within a call to sqlite3_value_text() (may fail if the db is utf-16))
2174fccf43aSdan ** SQLITE_NOMEM is returned.
2184fccf43aSdan */
2194fccf43aSdan static int sessionSerializeValue(
2204fccf43aSdan   u8 *aBuf,                       /* If non-NULL, write serialized value here */
2214fccf43aSdan   sqlite3_value *pValue,          /* Value to serialize */
2224fccf43aSdan   int *pnWrite                    /* IN/OUT: Increment by bytes written */
2234fccf43aSdan ){
224296c7658Sdan   int eType;                      /* Value type (SQLITE_NULL, TEXT etc.) */
225296c7658Sdan   int nByte;                      /* Size of serialized value in bytes */
2264fccf43aSdan 
2274fccf43aSdan   eType = sqlite3_value_type(pValue);
2284fccf43aSdan   if( aBuf ) aBuf[0] = eType;
2294fccf43aSdan 
2304fccf43aSdan   switch( eType ){
2314fccf43aSdan     case SQLITE_NULL:
2324fccf43aSdan       nByte = 1;
2334fccf43aSdan       break;
2344fccf43aSdan 
2354fccf43aSdan     case SQLITE_INTEGER:
2364fccf43aSdan     case SQLITE_FLOAT:
2374fccf43aSdan       if( aBuf ){
2384fccf43aSdan         /* TODO: SQLite does something special to deal with mixed-endian
2394fccf43aSdan         ** floating point values (e.g. ARM7). This code probably should
2404fccf43aSdan         ** too.  */
2414fccf43aSdan         u64 i;
2424fccf43aSdan         if( eType==SQLITE_INTEGER ){
2434fccf43aSdan           i = (u64)sqlite3_value_int64(pValue);
2444fccf43aSdan         }else{
2454fccf43aSdan           double r;
2464fccf43aSdan           assert( sizeof(double)==8 && sizeof(u64)==8 );
2474fccf43aSdan           r = sqlite3_value_double(pValue);
2484fccf43aSdan           memcpy(&i, &r, 8);
2494fccf43aSdan         }
250296c7658Sdan         sessionPutI64(&aBuf[1], i);
2514fccf43aSdan       }
2524fccf43aSdan       nByte = 9;
2534fccf43aSdan       break;
2544fccf43aSdan 
2554e895da1Sdan     default: {
2564fccf43aSdan       int n = sqlite3_value_bytes(pValue);
257296c7658Sdan       int nVarint = sessionVarintLen(n);
2584e895da1Sdan       assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
2594fccf43aSdan       if( aBuf ){
2604fccf43aSdan         sessionVarintPut(&aBuf[1], n);
2614fccf43aSdan         memcpy(&aBuf[nVarint + 1], eType==SQLITE_TEXT ?
2624fccf43aSdan             sqlite3_value_text(pValue) : sqlite3_value_blob(pValue), n
2634fccf43aSdan         );
2644fccf43aSdan       }
2654fccf43aSdan 
2664fccf43aSdan       nByte = 1 + nVarint + n;
2674fccf43aSdan       break;
2684fccf43aSdan     }
2694fccf43aSdan   }
2704fccf43aSdan 
2714fccf43aSdan   *pnWrite += nByte;
2724fccf43aSdan   return SQLITE_OK;
2734fccf43aSdan }
2744fccf43aSdan 
2754131639cSdan #define HASH_APPEND(hash, add) ((hash) << 3) ^ (hash) ^ (unsigned int)(add)
2764131639cSdan static unsigned int sessionHashAppendI64(unsigned int h, i64 i){
277e8d5648eSdan   h = HASH_APPEND(h, i & 0xFFFFFFFF);
278e8d5648eSdan   return HASH_APPEND(h, (i>>32)&0xFFFFFFFF);
279e8d5648eSdan }
2804131639cSdan static unsigned int sessionHashAppendBlob(unsigned int h, int n, const u8 *z){
281e8d5648eSdan   int i;
282e8d5648eSdan   for(i=0; i<n; i++) h = HASH_APPEND(h, z[i]);
283e8d5648eSdan   return h;
284e8d5648eSdan }
285e8d5648eSdan 
2864fccf43aSdan /*
2874131639cSdan ** This function may only be called from within a pre-update callback.
2884131639cSdan ** It calculates a hash based on the primary key values of the old.* or
2894131639cSdan ** new.* row currently available. The value returned is guaranteed to
2904131639cSdan ** be less than pTab->nBucket.
2914fccf43aSdan */
2924131639cSdan static unsigned int sessionPreupdateHash(
293e8d5648eSdan   sqlite3 *db,                    /* Database handle */
294e8d5648eSdan   SessionTable *pTab,             /* Session table handle */
295e8d5648eSdan   int bNew,                       /* True to hash the new.* PK */
29627453faeSdan   int *piHash,                    /* OUT: Hash value */
29727453faeSdan   int *pbNullPK
298e8d5648eSdan ){
2994131639cSdan   unsigned int h = 0;             /* Hash value to return */
3004131639cSdan   int i;                          /* Used to iterate through columns */
301e8d5648eSdan 
30227453faeSdan   assert( *pbNullPK==0 );
303e8d5648eSdan   assert( pTab->nCol==sqlite3_preupdate_count(db) );
304e8d5648eSdan   for(i=0; i<pTab->nCol; i++){
305e8d5648eSdan     if( pTab->abPK[i] ){
306e8d5648eSdan       int rc;
307e8d5648eSdan       int eType;
308e8d5648eSdan       sqlite3_value *pVal;
309e8d5648eSdan 
310e8d5648eSdan       if( bNew ){
311e8d5648eSdan         rc = sqlite3_preupdate_new(db, i, &pVal);
312e8d5648eSdan       }else{
313e8d5648eSdan         rc = sqlite3_preupdate_old(db, i, &pVal);
314e8d5648eSdan       }
31512ca0b56Sdan       if( rc!=SQLITE_OK ) return rc;
316e8d5648eSdan 
317e8d5648eSdan       eType = sqlite3_value_type(pVal);
318e8d5648eSdan       h = HASH_APPEND(h, eType);
319e8d5648eSdan       switch( eType ){
320e8d5648eSdan         case SQLITE_INTEGER:
321e8d5648eSdan         case SQLITE_FLOAT: {
322e8d5648eSdan           i64 iVal;
323e8d5648eSdan           if( eType==SQLITE_INTEGER ){
324e8d5648eSdan             iVal = sqlite3_value_int64(pVal);
325e8d5648eSdan           }else{
326e8d5648eSdan             double rVal = sqlite3_value_double(pVal);
327e8d5648eSdan             assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
328e8d5648eSdan             memcpy(&iVal, &rVal, 8);
329e8d5648eSdan           }
330e8d5648eSdan           h = sessionHashAppendI64(h, iVal);
331e8d5648eSdan           break;
332e8d5648eSdan         }
333e8d5648eSdan 
334e8d5648eSdan         case SQLITE_TEXT:
335e8d5648eSdan         case SQLITE_BLOB: {
336e8d5648eSdan           int n = sqlite3_value_bytes(pVal);
337e8d5648eSdan           const u8 *z = eType==SQLITE_TEXT ?
338e8d5648eSdan             sqlite3_value_text(pVal) : sqlite3_value_blob(pVal);
339e8d5648eSdan           h = sessionHashAppendBlob(h, n, z);
340e8d5648eSdan           break;
341e8d5648eSdan         }
34227453faeSdan 
34327453faeSdan         default:
34427453faeSdan           assert( eType==SQLITE_NULL );
34527453faeSdan           *pbNullPK = 1;
34627453faeSdan           return SQLITE_OK;
347e8d5648eSdan       }
348e8d5648eSdan     }
349e8d5648eSdan   }
350e8d5648eSdan 
351e8d5648eSdan   *piHash = (h % pTab->nChange);
352e8d5648eSdan   return SQLITE_OK;
353e8d5648eSdan }
354e8d5648eSdan 
3554131639cSdan /*
3564131639cSdan ** Based on the primary key values stored in change pChange, calculate a
3574131639cSdan ** hash key, assuming the has table has nBucket buckets. The hash keys
3584131639cSdan ** calculated by this function are compatible with those calculated by
3594131639cSdan ** sessionPreupdateHash().
3604131639cSdan */
3614131639cSdan static unsigned int sessionChangeHash(
3624131639cSdan   sqlite3 *db,                    /* Database handle */
3634131639cSdan   SessionTable *pTab,             /* Table handle */
3644131639cSdan   SessionChange *pChange,         /* Change handle */
3654131639cSdan   int nBucket                     /* Assume this many buckets in hash table */
366e8d5648eSdan ){
3674131639cSdan   unsigned int h = 0;             /* Value to return */
3684131639cSdan   int i;                          /* Used to iterate through columns */
3694131639cSdan   u8 *a = pChange->aRecord;       /* Used to iterate through change record */
370e8d5648eSdan 
371e8d5648eSdan   for(i=0; i<pTab->nCol; i++){
372e8d5648eSdan     int eType = *a++;
373e8d5648eSdan     int isPK = pTab->abPK[i];
374e8d5648eSdan 
37527453faeSdan     /* It is not possible for eType to be SQLITE_NULL here. The session
37627453faeSdan     ** module does not record changes for rows with NULL values stored in
37727453faeSdan     ** primary key columns. */
37827453faeSdan     assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
37927453faeSdan          || eType==SQLITE_TEXT || eType==SQLITE_BLOB
38027453faeSdan     );
38127453faeSdan 
382e8d5648eSdan     if( isPK ) h = HASH_APPEND(h, eType);
38327453faeSdan     if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
38427453faeSdan       if( isPK ) h = sessionHashAppendI64(h, sessionGetI64(a));
385e8d5648eSdan       a += 8;
38627453faeSdan     }else{
387e8d5648eSdan       int n;
388e8d5648eSdan       a += sessionVarintGet(a, &n);
38927453faeSdan       if( isPK ) h = sessionHashAppendBlob(h, n, a);
390e8d5648eSdan       a += n;
391e8d5648eSdan     }
392e8d5648eSdan   }
393e8d5648eSdan   return (h % nBucket);
394e8d5648eSdan }
395e8d5648eSdan 
396e8d5648eSdan static int sessionPreupdateEqual(
397e8d5648eSdan   sqlite3 *db,
398e8d5648eSdan   SessionTable *pTab,
399e8d5648eSdan   SessionChange *pChange,
400e8d5648eSdan   int bNew,
401e8d5648eSdan   int *pbEqual
402e8d5648eSdan ){
403e8d5648eSdan   int i;
404e8d5648eSdan   u8 *a = pChange->aRecord;
405e8d5648eSdan 
406e8d5648eSdan   *pbEqual = 0;
407e8d5648eSdan 
408e8d5648eSdan   for(i=0; i<pTab->nCol; i++){
409e8d5648eSdan     int eType = *a++;
410e8d5648eSdan     if( !pTab->abPK[i] ){
411e8d5648eSdan       switch( eType ){
412e8d5648eSdan         case SQLITE_INTEGER:
413e8d5648eSdan         case SQLITE_FLOAT:
414e8d5648eSdan           a += 8;
415e8d5648eSdan           break;
416e8d5648eSdan 
417e8d5648eSdan         case SQLITE_TEXT:
418e8d5648eSdan         case SQLITE_BLOB: {
419e8d5648eSdan           int n;
420e8d5648eSdan           a += sessionVarintGet(a, &n);
421e8d5648eSdan           a += n;
422e8d5648eSdan           break;
423e8d5648eSdan         }
424e8d5648eSdan       }
425e8d5648eSdan     }else{
426e8d5648eSdan       sqlite3_value *pVal;
427e8d5648eSdan       int rc;
428e8d5648eSdan       if( bNew ){
429e8d5648eSdan         rc = sqlite3_preupdate_new(db, i, &pVal);
430e8d5648eSdan       }else{
431e8d5648eSdan         rc = sqlite3_preupdate_old(db, i, &pVal);
432e8d5648eSdan       }
433e8d5648eSdan       if( rc!=SQLITE_OK || sqlite3_value_type(pVal)!=eType ) return rc;
434e8d5648eSdan 
43512ca0b56Sdan       /* A SessionChange object never has a NULL value in a PK column */
43612ca0b56Sdan       assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
43712ca0b56Sdan            || eType==SQLITE_BLOB    || eType==SQLITE_TEXT
43812ca0b56Sdan       );
43912ca0b56Sdan 
44012ca0b56Sdan       if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
441e8d5648eSdan         i64 iVal = sessionGetI64(a);
442e8d5648eSdan         a += 8;
443e8d5648eSdan         if( eType==SQLITE_INTEGER ){
444e8d5648eSdan           if( sqlite3_value_int64(pVal)!=iVal ) return SQLITE_OK;
445e8d5648eSdan         }else{
446e8d5648eSdan           double rVal;
447e8d5648eSdan           assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
448e8d5648eSdan           memcpy(&rVal, &iVal, 8);
449e8d5648eSdan           if( sqlite3_value_double(pVal)!=rVal ) return SQLITE_OK;
450e8d5648eSdan         }
45112ca0b56Sdan       }else{
452e8d5648eSdan         int n;
453e8d5648eSdan         const u8 *z;
454e8d5648eSdan         a += sessionVarintGet(a, &n);
455e8d5648eSdan         if( sqlite3_value_bytes(pVal)!=n ) return SQLITE_OK;
45612ca0b56Sdan         if( eType==SQLITE_TEXT ){
45712ca0b56Sdan           z = sqlite3_value_text(pVal);
45812ca0b56Sdan         }else{
45912ca0b56Sdan           z = sqlite3_value_blob(pVal);
46012ca0b56Sdan         }
461e8d5648eSdan         if( memcmp(a, z, n) ) return SQLITE_OK;
462e8d5648eSdan         a += n;
463e8d5648eSdan         break;
464e8d5648eSdan       }
465e8d5648eSdan     }
466e8d5648eSdan   }
467e8d5648eSdan 
468e8d5648eSdan   *pbEqual = 1;
469e8d5648eSdan   return SQLITE_OK;
4704fccf43aSdan }
4714fccf43aSdan 
4724fccf43aSdan /*
4734fccf43aSdan ** If required, grow the hash table used to store changes on table pTab
4744fccf43aSdan ** (part of the session pSession). If a fatal OOM error occurs, set the
4754fccf43aSdan ** session object to failed and return SQLITE_ERROR. Otherwise, return
4764fccf43aSdan ** SQLITE_OK.
4774fccf43aSdan **
4784fccf43aSdan ** It is possible that a non-fatal OOM error occurs in this function. In
4794fccf43aSdan ** that case the hash-table does not grow, but SQLITE_OK is returned anyway.
4804fccf43aSdan ** Growing the hash table in this case is a performance optimization only,
4814fccf43aSdan ** it is not required for correct operation.
4824fccf43aSdan */
4834fccf43aSdan static int sessionGrowHash(sqlite3_session *pSession, SessionTable *pTab){
4844fccf43aSdan   if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
4854fccf43aSdan     int i;
4864fccf43aSdan     SessionChange **apNew;
4874fccf43aSdan     int nNew = (pTab->nChange ? pTab->nChange : 128) * 2;
4884fccf43aSdan 
4894fccf43aSdan     apNew = (SessionChange **)sqlite3_malloc(sizeof(SessionChange *) * nNew);
4904fccf43aSdan     if( apNew==0 ){
4914fccf43aSdan       if( pTab->nChange==0 ){
4924fccf43aSdan         pSession->rc = SQLITE_NOMEM;
4934fccf43aSdan         return SQLITE_ERROR;
4944fccf43aSdan       }
4954fccf43aSdan       return SQLITE_OK;
4964fccf43aSdan     }
4974fccf43aSdan     memset(apNew, 0, sizeof(SessionChange *) * nNew);
4984fccf43aSdan 
4994fccf43aSdan     for(i=0; i<pTab->nChange; i++){
5004fccf43aSdan       SessionChange *p;
5014fccf43aSdan       SessionChange *pNext;
5024fccf43aSdan       for(p=pTab->apChange[i]; p; p=pNext){
503e8d5648eSdan         int iHash = sessionChangeHash(pSession->db, pTab, p, nNew);
5044fccf43aSdan         pNext = p->pNext;
5054fccf43aSdan         p->pNext = apNew[iHash];
5064fccf43aSdan         apNew[iHash] = p;
5074fccf43aSdan       }
5084fccf43aSdan     }
5094fccf43aSdan 
5104fccf43aSdan     sqlite3_free(pTab->apChange);
5114fccf43aSdan     pTab->nChange = nNew;
5124fccf43aSdan     pTab->apChange = apNew;
5134fccf43aSdan   }
5144fccf43aSdan 
5154fccf43aSdan   return SQLITE_OK;
5164fccf43aSdan }
5174fccf43aSdan 
518296c7658Sdan /*
519e8d5648eSdan ** This function queries the database for the names of the columns of table
520e8d5648eSdan ** zThis, in schema zDb. It is expected that the table has nCol columns. If
521e8d5648eSdan ** not, SQLITE_SCHEMA is returned and none of the output variables are
522e8d5648eSdan ** populated.
523e8d5648eSdan **
524e8d5648eSdan ** Otherwise, if it is not NULL, variable *pzTab is set to point to a
525e8d5648eSdan ** nul-terminated copy of the table name. *pazCol (if not NULL) is set to
526e8d5648eSdan ** point to an array of pointers to column names. And *pabPK (again, if not
527e8d5648eSdan ** NULL) is set to point to an array of booleans - true if the corresponding
528e8d5648eSdan ** column is part of the primary key.
529e8d5648eSdan **
530e8d5648eSdan ** For example, if the table is declared as:
531e8d5648eSdan **
532e8d5648eSdan **     CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z));
533e8d5648eSdan **
534e8d5648eSdan ** Then the three output variables are populated as follows:
535e8d5648eSdan **
536e8d5648eSdan **     *pzTab  = "tbl1"
537e8d5648eSdan **     *pazCol = {"w", "x", "y", "z"}
538e8d5648eSdan **     *pabPK  = {1, 0, 0, 1}
539e8d5648eSdan **
540e8d5648eSdan ** All returned buffers are part of the same single allocation, which must
541e8d5648eSdan ** be freed using sqlite3_free() by the caller. If pazCol was not NULL, then
542e8d5648eSdan ** pointer *pazCol should be freed to release all memory. Otherwise, pointer
543e8d5648eSdan ** *pabPK. It is illegal for both pazCol and pabPK to be NULL.
544e8d5648eSdan */
545e8d5648eSdan static int sessionTableInfo(
546e8d5648eSdan   sqlite3 *db,                    /* Database connection */
547e8d5648eSdan   const char *zDb,                /* Name of attached database (e.g. "main") */
548e8d5648eSdan   const char *zThis,              /* Table name */
549ca62ad57Sdan   int *pnCol,                     /* OUT: number of columns */
550e8d5648eSdan   const char **pzTab,             /* OUT: Copy of zThis */
551e8d5648eSdan   const char ***pazCol,           /* OUT: Array of column names for table */
552e8d5648eSdan   u8 **pabPK                      /* OUT: Array of booleans - true for PK col */
553e8d5648eSdan ){
554e8d5648eSdan   char *zPragma;
555e8d5648eSdan   sqlite3_stmt *pStmt;
556e8d5648eSdan   int rc;
557e8d5648eSdan   int nByte;
558e8d5648eSdan   int nDbCol = 0;
559e8d5648eSdan   int nThis;
560e8d5648eSdan   int i;
561e8d5648eSdan   u8 *pAlloc;
562db04571cSdan   char **azCol = 0;
563e8d5648eSdan   u8 *abPK;
564e8d5648eSdan 
565db04571cSdan   assert( pazCol && pabPK );
566e8d5648eSdan 
567*cfdbde21Sdrh   nThis = sqlite3Strlen30(zThis);
568e8d5648eSdan   zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
569e8d5648eSdan   if( !zPragma ) return SQLITE_NOMEM;
570e8d5648eSdan 
571e8d5648eSdan   rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
572e8d5648eSdan   sqlite3_free(zPragma);
573e8d5648eSdan   if( rc!=SQLITE_OK ) return rc;
574e8d5648eSdan 
575e8d5648eSdan   nByte = nThis + 1;
576e8d5648eSdan   while( SQLITE_ROW==sqlite3_step(pStmt) ){
577e8d5648eSdan     nByte += sqlite3_column_bytes(pStmt, 1);
578e8d5648eSdan     nDbCol++;
579e8d5648eSdan   }
580e8d5648eSdan   rc = sqlite3_reset(pStmt);
581e8d5648eSdan 
582e8d5648eSdan   if( rc==SQLITE_OK ){
583e8d5648eSdan     nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
584e8d5648eSdan     pAlloc = sqlite3_malloc(nByte);
585e8d5648eSdan     if( pAlloc==0 ){
586e8d5648eSdan       rc = SQLITE_NOMEM;
587e8d5648eSdan     }
588e8d5648eSdan   }
589e8d5648eSdan   if( rc==SQLITE_OK ){
590e8d5648eSdan     azCol = (char **)pAlloc;
591ca62ad57Sdan     pAlloc = (u8 *)&azCol[nDbCol];
592e8d5648eSdan     abPK = (u8 *)pAlloc;
593ca62ad57Sdan     pAlloc = &abPK[nDbCol];
594e8d5648eSdan     if( pzTab ){
595e8d5648eSdan       memcpy(pAlloc, zThis, nThis+1);
596e8d5648eSdan       *pzTab = (char *)pAlloc;
597e8d5648eSdan       pAlloc += nThis+1;
598e8d5648eSdan     }
599e8d5648eSdan 
600e8d5648eSdan     i = 0;
601e8d5648eSdan     while( SQLITE_ROW==sqlite3_step(pStmt) ){
602e8d5648eSdan       int nName = sqlite3_column_bytes(pStmt, 1);
603e8d5648eSdan       const unsigned char *zName = sqlite3_column_text(pStmt, 1);
604e8d5648eSdan       if( zName==0 ) break;
605e8d5648eSdan       memcpy(pAlloc, zName, nName+1);
606e8d5648eSdan       azCol[i] = (char *)pAlloc;
607e8d5648eSdan       pAlloc += nName+1;
608db04571cSdan       abPK[i] = sqlite3_column_int(pStmt, 5);
609e8d5648eSdan       i++;
610e8d5648eSdan     }
611e8d5648eSdan     rc = sqlite3_reset(pStmt);
612e8d5648eSdan 
613e8d5648eSdan   }
614e8d5648eSdan 
615e8d5648eSdan   /* If successful, populate the output variables. Otherwise, zero them and
616e8d5648eSdan   ** free any allocation made. An error code will be returned in this case.
617e8d5648eSdan   */
618e8d5648eSdan   if( rc==SQLITE_OK ){
619db04571cSdan     *pazCol = (const char **)azCol;
620db04571cSdan     *pabPK = abPK;
621ca62ad57Sdan     *pnCol = nDbCol;
622e8d5648eSdan   }else{
623db04571cSdan     *pazCol = 0;
624db04571cSdan     *pabPK = 0;
625ca62ad57Sdan     *pnCol = 0;
626e8d5648eSdan     if( pzTab ) *pzTab = 0;
627db04571cSdan     sqlite3_free(azCol);
628e8d5648eSdan   }
629e8d5648eSdan   sqlite3_finalize(pStmt);
630e8d5648eSdan   return rc;
631e8d5648eSdan }
632e8d5648eSdan 
633e8d5648eSdan /*
634296c7658Sdan ** This function is only called from within a pre-update handler for a
635296c7658Sdan ** write to table pTab, part of session pSession. If this is the first
636296c7658Sdan ** write to this table, set the SessionTable.nCol variable to the number
637296c7658Sdan ** of columns in the table.
638296c7658Sdan **
639296c7658Sdan ** Otherwise, if this is not the first time this table has been written
640296c7658Sdan ** to, check that the number of columns in the table has not changed. If
641296c7658Sdan ** it has not, return zero.
642296c7658Sdan **
643296c7658Sdan ** If the number of columns in the table has changed since the last write
644296c7658Sdan ** was recorded, set the session error-code to SQLITE_SCHEMA and return
645296c7658Sdan ** non-zero. Users are not allowed to change the number of columns in a table
646296c7658Sdan ** for which changes are being recorded by the session module. If they do so,
647296c7658Sdan ** it is an error.
648296c7658Sdan */
6494fccf43aSdan static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
6504fccf43aSdan   if( pTab->nCol==0 ){
651e8d5648eSdan     assert( pTab->azCol==0 || pTab->abPK==0 );
652e8d5648eSdan     pSession->rc = sessionTableInfo(pSession->db, pSession->zDb,
653ca62ad57Sdan         pTab->zName, &pTab->nCol, 0, &pTab->azCol, &pTab->abPK
654e8d5648eSdan     );
655ca62ad57Sdan   }
656ca62ad57Sdan   if( pSession->rc==SQLITE_OK
657ca62ad57Sdan    && pTab->nCol!=sqlite3_preupdate_count(pSession->db)
658ca62ad57Sdan   ){
6594fccf43aSdan     pSession->rc = SQLITE_SCHEMA;
6604fccf43aSdan   }
661e8d5648eSdan   return pSession->rc;
662e8d5648eSdan }
663e8d5648eSdan 
664e8d5648eSdan static void sessionPreupdateOneChange(
665e8d5648eSdan   int op,
666e8d5648eSdan   sqlite3_session *pSession,
667e8d5648eSdan   SessionTable *pTab
668e8d5648eSdan ){
669e8d5648eSdan   sqlite3 *db = pSession->db;
670e8d5648eSdan   int iHash;
67127453faeSdan   int bNullPk = 0;
672e8d5648eSdan   int rc = SQLITE_OK;
673e8d5648eSdan 
674e8d5648eSdan   if( pSession->rc ) return;
675e8d5648eSdan 
676e8d5648eSdan   /* Load table details if required */
677e8d5648eSdan   if( sessionInitTable(pSession, pTab) ) return;
678e8d5648eSdan 
679e8d5648eSdan   /* Grow the hash table if required */
680e8d5648eSdan   if( sessionGrowHash(pSession, pTab) ) return;
681e8d5648eSdan 
682e8d5648eSdan   /* Search the hash table for an existing entry for rowid=iKey2. If
683e8d5648eSdan   ** one is found, store a pointer to it in pChange and unlink it from
684e8d5648eSdan   ** the hash table. Otherwise, set pChange to NULL.
685e8d5648eSdan   */
68627453faeSdan   rc = sessionPreupdateHash(db, pTab, op==SQLITE_INSERT, &iHash, &bNullPk);
687b4480e94Sdan   if( rc==SQLITE_OK && bNullPk==0 ){
688b4480e94Sdan     SessionChange *pC;
689e8d5648eSdan     for(pC=pTab->apChange[iHash]; rc==SQLITE_OK && pC; pC=pC->pNext){
690e8d5648eSdan       int bEqual;
691e8d5648eSdan       rc = sessionPreupdateEqual(db, pTab, pC, op==SQLITE_INSERT, &bEqual);
692e8d5648eSdan       if( bEqual ) break;
693e8d5648eSdan     }
694e8d5648eSdan     if( pC==0 ){
695e8d5648eSdan       /* Create a new change object containing all the old values (if
696e8d5648eSdan        ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK
697e8d5648eSdan        ** values (if this is an INSERT). */
698b4480e94Sdan       SessionChange *pChange; /* New change object */
699e8d5648eSdan       int nByte;              /* Number of bytes to allocate */
700e8d5648eSdan       int i;                  /* Used to iterate through columns */
701e8d5648eSdan 
702b4480e94Sdan       assert( rc==SQLITE_OK );
703e8d5648eSdan       pTab->nEntry++;
704e8d5648eSdan 
705e8d5648eSdan       /* Figure out how large an allocation is required */
706e8d5648eSdan       nByte = sizeof(SessionChange);
707e8d5648eSdan       for(i=0; i<pTab->nCol && rc==SQLITE_OK; i++){
708e8d5648eSdan         sqlite3_value *p = 0;
709e8d5648eSdan         if( op!=SQLITE_INSERT ){
710e8d5648eSdan           rc = sqlite3_preupdate_old(pSession->db, i, &p);
711e8d5648eSdan         }else if( 1 || pTab->abPK[i] ){
712e8d5648eSdan           rc = sqlite3_preupdate_new(pSession->db, i, &p);
713e8d5648eSdan         }
714e8d5648eSdan         if( p && rc==SQLITE_OK ){
715e8d5648eSdan           rc = sessionSerializeValue(0, p, &nByte);
716e8d5648eSdan         }
717e8d5648eSdan       }
718e8d5648eSdan 
719e8d5648eSdan       /* Allocate the change object */
720e8d5648eSdan       pChange = (SessionChange *)sqlite3_malloc(nByte);
721e8d5648eSdan       if( !pChange ){
722e8d5648eSdan         rc = SQLITE_NOMEM;
723e8d5648eSdan       }else{
724e8d5648eSdan         memset(pChange, 0, sizeof(SessionChange));
725e8d5648eSdan         pChange->aRecord = (u8 *)&pChange[1];
726e8d5648eSdan       }
727e8d5648eSdan 
728e8d5648eSdan       /* Populate the change object */
729e8d5648eSdan       nByte = 0;
730e8d5648eSdan       for(i=0; i<pTab->nCol && rc==SQLITE_OK; i++){
731e8d5648eSdan         sqlite3_value *p = 0;
732e8d5648eSdan         if( op!=SQLITE_INSERT ){
733e8d5648eSdan           rc = sqlite3_preupdate_old(pSession->db, i, &p);
734e8d5648eSdan         }else if( 1 || pTab->abPK[i] ){
735e8d5648eSdan           rc = sqlite3_preupdate_new(pSession->db, i, &p);
736e8d5648eSdan         }
737e8d5648eSdan         if( p && rc==SQLITE_OK ){
738e8d5648eSdan           rc = sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
739e8d5648eSdan         }
740e8d5648eSdan       }
74112ca0b56Sdan       if( rc==SQLITE_OK ){
742e8d5648eSdan         /* Add the change back to the hash-table */
743b4480e94Sdan         if( pSession->bIndirect || sqlite3_preupdate_depth(pSession->db) ){
744b4480e94Sdan           pChange->bIndirect = 1;
745b4480e94Sdan         }
74612ca0b56Sdan         pChange->nRecord = nByte;
747e8d5648eSdan         pChange->bInsert = (op==SQLITE_INSERT);
748e8d5648eSdan         pChange->pNext = pTab->apChange[iHash];
749e8d5648eSdan         pTab->apChange[iHash] = pChange;
75012ca0b56Sdan       }else{
75112ca0b56Sdan         sqlite3_free(pChange);
752e8d5648eSdan       }
753b4480e94Sdan     }else if( rc==SQLITE_OK && pC->bIndirect ){
754b4480e94Sdan       /* If the existing change is considered "indirect", but this current
755b4480e94Sdan       ** change is "direct", mark the change object as direct. */
756b4480e94Sdan       if( sqlite3_preupdate_depth(pSession->db)==0 && pSession->bIndirect==0 ){
757b4480e94Sdan         pC->bIndirect = 0;
758b4480e94Sdan       }
759e8d5648eSdan     }
7604fccf43aSdan   }
76112ca0b56Sdan 
76212ca0b56Sdan   /* If an error has occurred, mark the session object as failed. */
76312ca0b56Sdan   if( rc!=SQLITE_OK ){
76412ca0b56Sdan     pSession->rc = rc;
76512ca0b56Sdan   }
76627453faeSdan }
7674fccf43aSdan 
7684fccf43aSdan /*
7694fccf43aSdan ** The 'pre-update' hook registered by this module with SQLite databases.
7704fccf43aSdan */
7714fccf43aSdan static void xPreUpdate(
7724fccf43aSdan   void *pCtx,                     /* Copy of third arg to preupdate_hook() */
7734fccf43aSdan   sqlite3 *db,                    /* Database handle */
7744fccf43aSdan   int op,                         /* SQLITE_UPDATE, DELETE or INSERT */
7754fccf43aSdan   char const *zDb,                /* Database name */
7764fccf43aSdan   char const *zName,              /* Table name */
7774fccf43aSdan   sqlite3_int64 iKey1,            /* Rowid of row about to be deleted/updated */
7784fccf43aSdan   sqlite3_int64 iKey2             /* New rowid value (for a rowid UPDATE) */
7794fccf43aSdan ){
7804fccf43aSdan   sqlite3_session *pSession;
781*cfdbde21Sdrh   int nDb = sqlite3Strlen30(zDb);
782*cfdbde21Sdrh   int nName = sqlite3Strlen30(zName);
7834fccf43aSdan 
7844c220252Sdan   assert( sqlite3_mutex_held(db->mutex) );
7854c220252Sdan 
7864fccf43aSdan   for(pSession=(sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){
7874fccf43aSdan     SessionTable *pTab;
788296c7658Sdan 
789e8d5648eSdan     /* If this session is attached to a different database ("main", "temp"
790e8d5648eSdan     ** etc.), or if it is not currently enabled, there is nothing to do. Skip
791e8d5648eSdan     ** to the next session object attached to this database. */
792296c7658Sdan     if( pSession->bEnable==0 ) continue;
7934fccf43aSdan     if( pSession->rc ) continue;
7944fccf43aSdan     if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue;
795296c7658Sdan 
796ff4d0f41Sdan     for(pTab=pSession->pTable; pTab || pSession->bAutoAttach; pTab=pTab->pNext){
797ff4d0f41Sdan       if( !pTab ){
798ff4d0f41Sdan         /* This branch is taken if table zName has not yet been attached to
799ff4d0f41Sdan         ** this session and the auto-attach flag is set.  */
800ff4d0f41Sdan         pSession->rc = sqlite3session_attach(pSession,zName);
801245b49b2Sdan         if( pSession->rc ) break;
802ff4d0f41Sdan         pTab = pSession->pTable;
803ff4d0f41Sdan         assert( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) );
804ff4d0f41Sdan       }
805ff4d0f41Sdan 
8064fccf43aSdan       if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ){
807e8d5648eSdan         sessionPreupdateOneChange(op, pSession, pTab);
808e8d5648eSdan         if( op==SQLITE_UPDATE ){
809e8d5648eSdan           sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab);
8104fccf43aSdan         }
8114fccf43aSdan         break;
8124fccf43aSdan       }
8134fccf43aSdan     }
8144fccf43aSdan   }
815296c7658Sdan }
8164fccf43aSdan 
8174fccf43aSdan /*
8184fccf43aSdan ** Create a session object. This session object will record changes to
8194fccf43aSdan ** database zDb attached to connection db.
8204fccf43aSdan */
8214fccf43aSdan int sqlite3session_create(
8224fccf43aSdan   sqlite3 *db,                    /* Database handle */
8234fccf43aSdan   const char *zDb,                /* Name of db (e.g. "main") */
8244fccf43aSdan   sqlite3_session **ppSession     /* OUT: New session object */
8254fccf43aSdan ){
826296c7658Sdan   sqlite3_session *pNew;          /* Newly allocated session object */
827296c7658Sdan   sqlite3_session *pOld;          /* Session object already attached to db */
828*cfdbde21Sdrh   int nDb = sqlite3Strlen30(zDb); /* Length of zDb in bytes */
8294fccf43aSdan 
830296c7658Sdan   /* Zero the output value in case an error occurs. */
8314fccf43aSdan   *ppSession = 0;
8324fccf43aSdan 
8334fccf43aSdan   /* Allocate and populate the new session object. */
8344fccf43aSdan   pNew = (sqlite3_session *)sqlite3_malloc(sizeof(sqlite3_session) + nDb + 1);
8354fccf43aSdan   if( !pNew ) return SQLITE_NOMEM;
8364fccf43aSdan   memset(pNew, 0, sizeof(sqlite3_session));
8374fccf43aSdan   pNew->db = db;
8384fccf43aSdan   pNew->zDb = (char *)&pNew[1];
839296c7658Sdan   pNew->bEnable = 1;
8404fccf43aSdan   memcpy(pNew->zDb, zDb, nDb+1);
8414fccf43aSdan 
8424fccf43aSdan   /* Add the new session object to the linked list of session objects
8434fccf43aSdan   ** attached to database handle $db. Do this under the cover of the db
8444fccf43aSdan   ** handle mutex.  */
8454fccf43aSdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
8464fccf43aSdan   pOld = (sqlite3_session*)sqlite3_preupdate_hook(db, xPreUpdate, (void*)pNew);
8474fccf43aSdan   pNew->pNext = pOld;
8484fccf43aSdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
8494fccf43aSdan 
8504fccf43aSdan   *ppSession = pNew;
8514fccf43aSdan   return SQLITE_OK;
8524fccf43aSdan }
8534fccf43aSdan 
8544fccf43aSdan /*
8554fccf43aSdan ** Delete a session object previously allocated using sqlite3session_create().
8564fccf43aSdan */
8574fccf43aSdan void sqlite3session_delete(sqlite3_session *pSession){
8584fccf43aSdan   sqlite3 *db = pSession->db;
8594fccf43aSdan   sqlite3_session *pHead;
8604fccf43aSdan   sqlite3_session **pp;
8614fccf43aSdan 
862296c7658Sdan   /* Unlink the session from the linked list of sessions attached to the
863296c7658Sdan   ** database handle. Hold the db mutex while doing so.  */
8644fccf43aSdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
8654fccf43aSdan   pHead = (sqlite3_session*)sqlite3_preupdate_hook(db, 0, 0);
8664fccf43aSdan   for(pp=&pHead; (*pp)!=pSession; pp=&((*pp)->pNext));
8674fccf43aSdan   *pp = (*pp)->pNext;
8684fccf43aSdan   if( pHead ) sqlite3_preupdate_hook(db, xPreUpdate, (void *)pHead);
8694fccf43aSdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
8704fccf43aSdan 
871296c7658Sdan   /* Delete all attached table objects. And the contents of their
872296c7658Sdan   ** associated hash-tables. */
8734fccf43aSdan   while( pSession->pTable ){
8744fccf43aSdan     int i;
8754fccf43aSdan     SessionTable *pTab = pSession->pTable;
8764fccf43aSdan     pSession->pTable = pTab->pNext;
8774fccf43aSdan     for(i=0; i<pTab->nChange; i++){
8784fccf43aSdan       SessionChange *p;
8794fccf43aSdan       SessionChange *pNext;
8804fccf43aSdan       for(p=pTab->apChange[i]; p; p=pNext){
8814fccf43aSdan         pNext = p->pNext;
8824fccf43aSdan         sqlite3_free(p);
8834fccf43aSdan       }
8844fccf43aSdan     }
885*cfdbde21Sdrh     sqlite3_free((char*)pTab->azCol);  /* cast works around VC++ bug */
8864fccf43aSdan     sqlite3_free(pTab->apChange);
8874fccf43aSdan     sqlite3_free(pTab);
8884fccf43aSdan   }
8894fccf43aSdan 
890296c7658Sdan   /* Free the session object itself. */
8914fccf43aSdan   sqlite3_free(pSession);
8924fccf43aSdan }
8934fccf43aSdan 
8944fccf43aSdan /*
8954fccf43aSdan ** Attach a table to a session. All subsequent changes made to the table
8964fccf43aSdan ** while the session object is enabled will be recorded.
8974fccf43aSdan **
8984fccf43aSdan ** Only tables that have a PRIMARY KEY defined may be attached. It does
8994fccf43aSdan ** not matter if the PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias)
9004fccf43aSdan ** or not.
9014fccf43aSdan */
9024fccf43aSdan int sqlite3session_attach(
9034fccf43aSdan   sqlite3_session *pSession,      /* Session object */
9044fccf43aSdan   const char *zName               /* Table name */
9054fccf43aSdan ){
906ff4d0f41Sdan   int rc = SQLITE_OK;
907ff4d0f41Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
908ff4d0f41Sdan 
909ff4d0f41Sdan   if( !zName ){
910ff4d0f41Sdan     pSession->bAutoAttach = 1;
911ff4d0f41Sdan   }else{
912296c7658Sdan     SessionTable *pTab;           /* New table object (if required) */
913296c7658Sdan     int nName;                    /* Number of bytes in string zName */
9144fccf43aSdan 
9154fccf43aSdan     /* First search for an existing entry. If one is found, this call is
9164fccf43aSdan     ** a no-op. Return early. */
917*cfdbde21Sdrh     nName = sqlite3Strlen30(zName);
9184fccf43aSdan     for(pTab=pSession->pTable; pTab; pTab=pTab->pNext){
9194c220252Sdan       if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ) break;
9204fccf43aSdan     }
9214fccf43aSdan 
9224c220252Sdan     if( !pTab ){
9234fccf43aSdan       /* Allocate new SessionTable object. */
9244fccf43aSdan       pTab = (SessionTable *)sqlite3_malloc(sizeof(SessionTable) + nName + 1);
9254c220252Sdan       if( !pTab ){
9264c220252Sdan         rc = SQLITE_NOMEM;
9274c220252Sdan       }else{
9284fccf43aSdan         /* Populate the new SessionTable object and link it into the list. */
9294fccf43aSdan         memset(pTab, 0, sizeof(SessionTable));
9304fccf43aSdan         pTab->zName = (char *)&pTab[1];
9314fccf43aSdan         memcpy(pTab->zName, zName, nName+1);
9324fccf43aSdan         pTab->pNext = pSession->pTable;
9334fccf43aSdan         pSession->pTable = pTab;
9344c220252Sdan       }
9354c220252Sdan     }
936ff4d0f41Sdan   }
9374fccf43aSdan 
9384c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
9394c220252Sdan   return rc;
9404fccf43aSdan }
9414fccf43aSdan 
942296c7658Sdan /*
943296c7658Sdan ** Ensure that there is room in the buffer to append nByte bytes of data.
944296c7658Sdan ** If not, use sqlite3_realloc() to grow the buffer so that there is.
945296c7658Sdan **
946296c7658Sdan ** If successful, return zero. Otherwise, if an OOM condition is encountered,
947296c7658Sdan ** set *pRc to SQLITE_NOMEM and return non-zero.
948296c7658Sdan */
9494fccf43aSdan static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){
9504fccf43aSdan   if( p->nAlloc-p->nBuf<nByte ){
9514fccf43aSdan     u8 *aNew;
9524fccf43aSdan     int nNew = p->nAlloc ? p->nAlloc : 128;
9534fccf43aSdan     do {
9544fccf43aSdan       nNew = nNew*2;
9554fccf43aSdan     }while( nNew<(p->nAlloc+nByte) );
9564fccf43aSdan 
9574fccf43aSdan     aNew = (u8 *)sqlite3_realloc(p->aBuf, nNew);
9584fccf43aSdan     if( 0==aNew ){
9594fccf43aSdan       *pRc = SQLITE_NOMEM;
9604fccf43aSdan       return 1;
9614fccf43aSdan     }
9624fccf43aSdan     p->aBuf = aNew;
9634fccf43aSdan     p->nAlloc = nNew;
9644fccf43aSdan   }
9654fccf43aSdan   return 0;
9664fccf43aSdan }
9674fccf43aSdan 
968296c7658Sdan /*
969296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
970296c7658Sdan ** called. Otherwise, append a single byte to the buffer.
971296c7658Sdan **
972296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
973296c7658Sdan ** returning.
974296c7658Sdan */
9754fccf43aSdan static void sessionAppendByte(SessionBuffer *p, u8 v, int *pRc){
9764fccf43aSdan   if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, 1, pRc) ){
9774fccf43aSdan     p->aBuf[p->nBuf++] = v;
9784fccf43aSdan   }
9794fccf43aSdan }
9804fccf43aSdan 
981296c7658Sdan /*
982296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
983296c7658Sdan ** called. Otherwise, append a single varint to the buffer.
984296c7658Sdan **
985296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
986296c7658Sdan ** returning.
987296c7658Sdan */
988*cfdbde21Sdrh static void sessionAppendVarint(SessionBuffer *p, int v, int *pRc){
9894fccf43aSdan   if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, 9, pRc) ){
9904fccf43aSdan     p->nBuf += sessionVarintPut(&p->aBuf[p->nBuf], v);
9914fccf43aSdan   }
9924fccf43aSdan }
9934fccf43aSdan 
994296c7658Sdan /*
995296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
996296c7658Sdan ** called. Otherwise, append a blob of data to the buffer.
997296c7658Sdan **
998296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
999296c7658Sdan ** returning.
1000296c7658Sdan */
10014fccf43aSdan static void sessionAppendBlob(
10024fccf43aSdan   SessionBuffer *p,
10034fccf43aSdan   const u8 *aBlob,
10044fccf43aSdan   int nBlob,
10054fccf43aSdan   int *pRc
10064fccf43aSdan ){
10074fccf43aSdan   if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nBlob, pRc) ){
10084fccf43aSdan     memcpy(&p->aBuf[p->nBuf], aBlob, nBlob);
10094fccf43aSdan     p->nBuf += nBlob;
10104fccf43aSdan   }
10114fccf43aSdan }
10124fccf43aSdan 
1013296c7658Sdan /*
1014296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1015296c7658Sdan ** called. Otherwise, append a string to the buffer. All bytes in the string
1016296c7658Sdan ** up to (but not including) the nul-terminator are written to the buffer.
1017296c7658Sdan **
1018296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1019296c7658Sdan ** returning.
1020296c7658Sdan */
1021d5f0767cSdan static void sessionAppendStr(
1022d5f0767cSdan   SessionBuffer *p,
1023d5f0767cSdan   const char *zStr,
1024d5f0767cSdan   int *pRc
1025d5f0767cSdan ){
1026*cfdbde21Sdrh   int nStr = sqlite3Strlen30(zStr);
1027d5f0767cSdan   if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nStr, pRc) ){
1028d5f0767cSdan     memcpy(&p->aBuf[p->nBuf], zStr, nStr);
1029d5f0767cSdan     p->nBuf += nStr;
1030d5f0767cSdan   }
1031d5f0767cSdan }
1032d5f0767cSdan 
1033296c7658Sdan /*
1034296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1035296c7658Sdan ** called. Otherwise, append the string representation of integer iVal
1036296c7658Sdan ** to the buffer. No nul-terminator is written.
1037296c7658Sdan **
1038296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1039296c7658Sdan ** returning.
1040296c7658Sdan */
1041d5f0767cSdan static void sessionAppendInteger(
1042296c7658Sdan   SessionBuffer *p,               /* Buffer to append to */
1043296c7658Sdan   int iVal,                       /* Value to write the string rep. of */
1044296c7658Sdan   int *pRc                        /* IN/OUT: Error code */
1045d5f0767cSdan ){
1046d5f0767cSdan   char aBuf[24];
1047d5f0767cSdan   sqlite3_snprintf(sizeof(aBuf)-1, aBuf, "%d", iVal);
1048d5f0767cSdan   sessionAppendStr(p, aBuf, pRc);
1049d5f0767cSdan }
1050d5f0767cSdan 
1051296c7658Sdan /*
1052296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1053296c7658Sdan ** called. Otherwise, append the string zStr enclosed in quotes (") and
1054296c7658Sdan ** with any embedded quote characters escaped to the buffer. No
1055296c7658Sdan ** nul-terminator byte is written.
1056296c7658Sdan **
1057296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
1058296c7658Sdan ** returning.
1059296c7658Sdan */
1060d5f0767cSdan static void sessionAppendIdent(
1061296c7658Sdan   SessionBuffer *p,               /* Buffer to a append to */
1062296c7658Sdan   const char *zStr,               /* String to quote, escape and append */
1063296c7658Sdan   int *pRc                        /* IN/OUT: Error code */
1064d5f0767cSdan ){
1065*cfdbde21Sdrh   int nStr = sqlite3Strlen30(zStr)*2 + 2 + 1;
1066d5f0767cSdan   if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nStr, pRc) ){
1067d5f0767cSdan     char *zOut = (char *)&p->aBuf[p->nBuf];
1068d5f0767cSdan     const char *zIn = zStr;
1069d5f0767cSdan     *zOut++ = '"';
1070d5f0767cSdan     while( *zIn ){
1071d5f0767cSdan       if( *zIn=='"' ) *zOut++ = '"';
1072d5f0767cSdan       *zOut++ = *(zIn++);
1073d5f0767cSdan     }
1074d5f0767cSdan     *zOut++ = '"';
1075*cfdbde21Sdrh     p->nBuf = (int)((u8 *)zOut - p->aBuf);
1076d5f0767cSdan   }
1077d5f0767cSdan }
1078d5f0767cSdan 
1079296c7658Sdan /*
1080296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1081296c7658Sdan ** called. Otherwse, it appends the serialized version of the value stored
1082296c7658Sdan ** in column iCol of the row that SQL statement pStmt currently points
1083296c7658Sdan ** to to the buffer.
1084296c7658Sdan */
10854fccf43aSdan static void sessionAppendCol(
1086296c7658Sdan   SessionBuffer *p,               /* Buffer to append to */
1087296c7658Sdan   sqlite3_stmt *pStmt,            /* Handle pointing to row containing value */
1088296c7658Sdan   int iCol,                       /* Column to read value from */
1089296c7658Sdan   int *pRc                        /* IN/OUT: Error code */
10904fccf43aSdan ){
10914fccf43aSdan   if( *pRc==SQLITE_OK ){
10924fccf43aSdan     int eType = sqlite3_column_type(pStmt, iCol);
10934fccf43aSdan     sessionAppendByte(p, (u8)eType, pRc);
10944fccf43aSdan     if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
10954fccf43aSdan       sqlite3_int64 i;
10964fccf43aSdan       u8 aBuf[8];
10974fccf43aSdan       if( eType==SQLITE_INTEGER ){
10984fccf43aSdan         i = sqlite3_column_int64(pStmt, iCol);
10994fccf43aSdan       }else{
11004fccf43aSdan         double r = sqlite3_column_double(pStmt, iCol);
11014fccf43aSdan         memcpy(&i, &r, 8);
11024fccf43aSdan       }
1103296c7658Sdan       sessionPutI64(aBuf, i);
11044fccf43aSdan       sessionAppendBlob(p, aBuf, 8, pRc);
11054fccf43aSdan     }
11064fccf43aSdan     if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){
11074fccf43aSdan       int nByte = sqlite3_column_bytes(pStmt, iCol);
11084fccf43aSdan       sessionAppendVarint(p, nByte, pRc);
11094fccf43aSdan       sessionAppendBlob(p, eType==SQLITE_BLOB ?
11104fccf43aSdan         sqlite3_column_blob(pStmt, iCol) : sqlite3_column_text(pStmt, iCol),
11114fccf43aSdan         nByte, pRc
11124fccf43aSdan       );
11134fccf43aSdan     }
11144fccf43aSdan   }
11154fccf43aSdan }
11164fccf43aSdan 
1117296c7658Sdan /*
1118296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is
1119296c7658Sdan ** called.
1120296c7658Sdan **
1121296c7658Sdan ** Otherwse, if *pRc is SQLITE_OK, then it appends an update change to
1122296c7658Sdan ** the buffer (see the comments under "CHANGESET FORMAT" at the top of the
1123296c7658Sdan ** file). An update change consists of:
1124296c7658Sdan **
1125296c7658Sdan **   1 byte:  SQLITE_UPDATE (0x17)
1126296c7658Sdan **   n bytes: old.* record (see RECORD FORMAT)
1127296c7658Sdan **   m bytes: new.* record (see RECORD FORMAT)
1128296c7658Sdan **
1129296c7658Sdan ** The SessionChange object passed as the third argument contains the
1130296c7658Sdan ** values that were stored in the row when the session began (the old.*
1131296c7658Sdan ** values). The statement handle passed as the second argument points
1132296c7658Sdan ** at the current version of the row (the new.* values).
1133296c7658Sdan **
1134296c7658Sdan ** If all of the old.* values are equal to their corresponding new.* value
1135296c7658Sdan ** (i.e. nothing has changed), then no data at all is appended to the buffer.
1136296c7658Sdan **
1137296c7658Sdan ** Otherwise, the old.* record contains all primary key values and the
1138296c7658Sdan ** original values of any fields that have been modified. The new.* record
1139296c7658Sdan ** contains the new values of only those fields that have been modified.
1140296c7658Sdan */
11414fccf43aSdan static void sessionAppendUpdate(
1142296c7658Sdan   SessionBuffer *pBuf,            /* Buffer to append to */
1143296c7658Sdan   sqlite3_stmt *pStmt,            /* Statement handle pointing at new row */
1144296c7658Sdan   SessionChange *p,               /* Object containing old values */
1145296c7658Sdan   u8 *abPK,                       /* Boolean array - true for PK columns */
1146296c7658Sdan   int *pRc                        /* IN/OUT: Error code */
11474fccf43aSdan ){
11484fccf43aSdan   if( *pRc==SQLITE_OK ){
1149296c7658Sdan     SessionBuffer buf2 = {0,0,0}; /* Buffer to accumulate new.* record in */
1150296c7658Sdan     int bNoop = 1;                /* Set to zero if any values are modified */
11511f34f8ccSdan     int nRewind = pBuf->nBuf;     /* Set to zero if any values are modified */
1152296c7658Sdan     int i;                        /* Used to iterate through columns */
1153296c7658Sdan     u8 *pCsr = p->aRecord;        /* Used to iterate through old.* values */
1154296c7658Sdan 
11554fccf43aSdan     sessionAppendByte(pBuf, SQLITE_UPDATE, pRc);
1156b4480e94Sdan     sessionAppendByte(pBuf, p->bIndirect, pRc);
11574fccf43aSdan     for(i=0; i<sqlite3_column_count(pStmt); i++){
115837f133ecSdan       int bChanged = 0;
11594fccf43aSdan       int nAdvance;
11604fccf43aSdan       int eType = *pCsr;
11614fccf43aSdan       switch( eType ){
11624fccf43aSdan         case SQLITE_NULL:
11634fccf43aSdan           nAdvance = 1;
11644fccf43aSdan           if( sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){
116537f133ecSdan             bChanged = 1;
11664fccf43aSdan           }
11674fccf43aSdan           break;
11684fccf43aSdan 
11694fccf43aSdan         case SQLITE_FLOAT:
11704fccf43aSdan         case SQLITE_INTEGER: {
11714fccf43aSdan           nAdvance = 9;
11724fccf43aSdan           if( eType==sqlite3_column_type(pStmt, i) ){
11734fccf43aSdan             sqlite3_int64 iVal = sessionGetI64(&pCsr[1]);
11744fccf43aSdan             if( eType==SQLITE_INTEGER ){
11754fccf43aSdan               if( iVal==sqlite3_column_int64(pStmt, i) ) break;
11764fccf43aSdan             }else{
11774fccf43aSdan               double dVal;
11784fccf43aSdan               memcpy(&dVal, &iVal, 8);
11794fccf43aSdan               if( dVal==sqlite3_column_double(pStmt, i) ) break;
11804fccf43aSdan             }
11814fccf43aSdan           }
118237f133ecSdan           bChanged = 1;
11834fccf43aSdan           break;
11844fccf43aSdan         }
11854fccf43aSdan 
1186e5754eecSdan         default: {
11874fccf43aSdan           int nByte;
11884fccf43aSdan           int nHdr = 1 + sessionVarintGet(&pCsr[1], &nByte);
1189e5754eecSdan           assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
11904fccf43aSdan           nAdvance = nHdr + nByte;
11914fccf43aSdan           if( eType==sqlite3_column_type(pStmt, i)
11924fccf43aSdan            && nByte==sqlite3_column_bytes(pStmt, i)
11934fccf43aSdan            && 0==memcmp(&pCsr[nHdr], sqlite3_column_blob(pStmt, i), nByte)
11944fccf43aSdan           ){
11954fccf43aSdan             break;
11964fccf43aSdan           }
119737f133ecSdan           bChanged = 1;
11984fccf43aSdan         }
11994fccf43aSdan       }
12004fccf43aSdan 
120137f133ecSdan       if( bChanged || abPK[i] ){
120237f133ecSdan         sessionAppendBlob(pBuf, pCsr, nAdvance, pRc);
12034fccf43aSdan       }else{
120437f133ecSdan         sessionAppendByte(pBuf, 0, pRc);
120537f133ecSdan       }
120637f133ecSdan 
120737f133ecSdan       if( bChanged ){
12084fccf43aSdan         sessionAppendCol(&buf2, pStmt, i, pRc);
12094fccf43aSdan         bNoop = 0;
121037f133ecSdan       }else{
121137f133ecSdan         sessionAppendByte(&buf2, 0, pRc);
12124fccf43aSdan       }
121337f133ecSdan 
12144fccf43aSdan       pCsr += nAdvance;
12154fccf43aSdan     }
12164fccf43aSdan 
12174fccf43aSdan     if( bNoop ){
12181f34f8ccSdan       pBuf->nBuf = nRewind;
12194fccf43aSdan     }else{
12204fccf43aSdan       sessionAppendBlob(pBuf, buf2.aBuf, buf2.nBuf, pRc);
12214fccf43aSdan     }
12221f34f8ccSdan     sqlite3_free(buf2.aBuf);
12234fccf43aSdan   }
1224d5f0767cSdan }
12254fccf43aSdan 
1226e8d5648eSdan static int sessionSelectStmt(
1227e8d5648eSdan   sqlite3 *db,                    /* Database handle */
1228d7fb7d24Sdan   const char *zDb,                /* Database name */
1229e8d5648eSdan   const char *zTab,               /* Table name */
1230e8d5648eSdan   int nCol,
1231e8d5648eSdan   const char **azCol,
1232e8d5648eSdan   u8 *abPK,
1233e8d5648eSdan   sqlite3_stmt **ppStmt
1234d5f0767cSdan ){
1235e8d5648eSdan   int rc = SQLITE_OK;
1236d5f0767cSdan   int i;
1237e8d5648eSdan   const char *zSep = "";
1238e8d5648eSdan   SessionBuffer buf = {0, 0, 0};
1239d5f0767cSdan 
1240e8d5648eSdan   sessionAppendStr(&buf, "SELECT * FROM ", &rc);
1241d7fb7d24Sdan   sessionAppendIdent(&buf, zDb, &rc);
1242d7fb7d24Sdan   sessionAppendStr(&buf, ".", &rc);
1243e8d5648eSdan   sessionAppendIdent(&buf, zTab, &rc);
1244e8d5648eSdan   sessionAppendStr(&buf, " WHERE ", &rc);
1245e8d5648eSdan   for(i=0; i<nCol; i++){
1246e8d5648eSdan     if( abPK[i] ){
1247e8d5648eSdan       sessionAppendStr(&buf, zSep, &rc);
1248e8d5648eSdan       sessionAppendIdent(&buf, azCol[i], &rc);
1249e8d5648eSdan       sessionAppendStr(&buf, " = ?", &rc);
1250e8d5648eSdan       sessionAppendInteger(&buf, i+1, &rc);
1251e8d5648eSdan       zSep = " AND ";
1252d5f0767cSdan     }
1253d5f0767cSdan   }
1254d5f0767cSdan   if( rc==SQLITE_OK ){
1255e8d5648eSdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, ppStmt, 0);
1256d5f0767cSdan   }
1257e8d5648eSdan   sqlite3_free(buf.aBuf);
1258e8d5648eSdan   return rc;
1259d5f0767cSdan }
1260d5f0767cSdan 
1261e8d5648eSdan static int sessionSelectBind(
1262e8d5648eSdan   sqlite3_stmt *pSelect,
1263e8d5648eSdan   int nCol,
1264e8d5648eSdan   u8 *abPK,
1265e5754eecSdan   SessionChange *pChange
1266e8d5648eSdan ){
1267e8d5648eSdan   int i;
1268e8d5648eSdan   int rc = SQLITE_OK;
1269e5754eecSdan   u8 *a = pChange->aRecord;
1270d5f0767cSdan 
1271e8d5648eSdan   for(i=0; i<nCol && rc==SQLITE_OK; i++){
1272e8d5648eSdan     int eType = *a++;
1273e8d5648eSdan 
1274e8d5648eSdan     switch( eType ){
1275e8d5648eSdan       case SQLITE_NULL:
1276e5754eecSdan         assert( abPK[i]==0 );
1277e8d5648eSdan         break;
1278e8d5648eSdan 
1279e8d5648eSdan       case SQLITE_INTEGER: {
1280e8d5648eSdan         if( abPK[i] ){
1281e8d5648eSdan           i64 iVal = sessionGetI64(a);
1282e8d5648eSdan           rc = sqlite3_bind_int64(pSelect, i+1, iVal);
1283e8d5648eSdan         }
1284e8d5648eSdan         a += 8;
1285e8d5648eSdan         break;
1286d5f0767cSdan       }
1287296c7658Sdan 
1288e8d5648eSdan       case SQLITE_FLOAT: {
1289e8d5648eSdan         if( abPK[i] ){
1290e8d5648eSdan           double rVal;
1291e8d5648eSdan           i64 iVal = sessionGetI64(a);
1292e8d5648eSdan           memcpy(&rVal, &iVal, 8);
12934e895da1Sdan           rc = sqlite3_bind_double(pSelect, i+1, rVal);
1294d5f0767cSdan         }
1295e8d5648eSdan         a += 8;
1296e8d5648eSdan         break;
1297e8d5648eSdan       }
1298e8d5648eSdan 
1299e8d5648eSdan       case SQLITE_TEXT: {
1300e8d5648eSdan         int n;
1301e8d5648eSdan         a += sessionVarintGet(a, &n);
1302e8d5648eSdan         if( abPK[i] ){
1303e8d5648eSdan           rc = sqlite3_bind_text(pSelect, i+1, (char *)a, n, SQLITE_TRANSIENT);
1304e8d5648eSdan         }
1305e8d5648eSdan         a += n;
1306e8d5648eSdan         break;
1307e8d5648eSdan       }
1308e8d5648eSdan 
1309e5754eecSdan       default: {
1310e8d5648eSdan         int n;
1311e5754eecSdan         assert( eType==SQLITE_BLOB );
1312e8d5648eSdan         a += sessionVarintGet(a, &n);
1313e8d5648eSdan         if( abPK[i] ){
1314e8d5648eSdan           rc = sqlite3_bind_blob(pSelect, i+1, a, n, SQLITE_TRANSIENT);
1315e8d5648eSdan         }
1316e8d5648eSdan         a += n;
1317e8d5648eSdan         break;
1318e8d5648eSdan       }
1319e8d5648eSdan     }
1320e8d5648eSdan   }
1321e8d5648eSdan 
1322d5f0767cSdan   return rc;
13234fccf43aSdan }
13244fccf43aSdan 
13254fccf43aSdan /*
13264fccf43aSdan ** Obtain a changeset object containing all changes recorded by the
13274fccf43aSdan ** session object passed as the first argument.
13284fccf43aSdan **
13294fccf43aSdan ** It is the responsibility of the caller to eventually free the buffer
13304fccf43aSdan ** using sqlite3_free().
13314fccf43aSdan */
13324fccf43aSdan int sqlite3session_changeset(
13334fccf43aSdan   sqlite3_session *pSession,      /* Session object */
13344fccf43aSdan   int *pnChangeset,               /* OUT: Size of buffer at *ppChangeset */
13354fccf43aSdan   void **ppChangeset              /* OUT: Buffer containing changeset */
13364fccf43aSdan ){
1337296c7658Sdan   sqlite3 *db = pSession->db;     /* Source database handle */
1338296c7658Sdan   SessionTable *pTab;             /* Used to iterate through attached tables */
1339296c7658Sdan   SessionBuffer buf = {0,0,0};    /* Buffer in which to accumlate changeset */
1340296c7658Sdan   int rc;                         /* Return code */
13414fccf43aSdan 
1342296c7658Sdan   /* Zero the output variables in case an error occurs. If this session
1343296c7658Sdan   ** object is already in the error state (sqlite3_session.rc != SQLITE_OK),
1344296c7658Sdan   ** this call will be a no-op.  */
13454fccf43aSdan   *pnChangeset = 0;
13464fccf43aSdan   *ppChangeset = 0;
1347e5754eecSdan 
1348e5754eecSdan   if( pSession->rc ) return pSession->rc;
1349e5754eecSdan   rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0);
1350e5754eecSdan   if( rc!=SQLITE_OK ) return rc;
1351e5754eecSdan 
1352e5754eecSdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
13534fccf43aSdan 
13544fccf43aSdan   for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
13554fccf43aSdan     if( pTab->nEntry ){
1356d7fb7d24Sdan       const char *zName = pTab->zName;
1357a9605b91Sdan       int nCol;                   /* Number of columns in table */
1358a9605b91Sdan       u8 *abPK;                   /* Primary key array */
1359a9605b91Sdan       const char **azCol = 0;     /* Table columns */
13601f34f8ccSdan       int i;                      /* Used to iterate through hash buckets */
13611f34f8ccSdan       sqlite3_stmt *pSel = 0;     /* SELECT statement to query table pTab */
13621f34f8ccSdan       int nRewind = buf.nBuf;     /* Initial size of write buffer */
13631f34f8ccSdan       int nNoop;                  /* Size of buffer after writing tbl header */
13644fccf43aSdan 
1365a9605b91Sdan       /* Check the table schema is still Ok. */
1366a9605b91Sdan       rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK);
1367a9605b91Sdan       if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){
1368a9605b91Sdan         rc = SQLITE_SCHEMA;
1369a9605b91Sdan       }
1370a9605b91Sdan 
13714fccf43aSdan       /* Write a table header */
13724fccf43aSdan       sessionAppendByte(&buf, 'T', &rc);
1373e8d5648eSdan       sessionAppendVarint(&buf, nCol, &rc);
1374244593c8Sdan       sessionAppendBlob(&buf, pTab->abPK, nCol, &rc);
1375*cfdbde21Sdrh       sessionAppendBlob(&buf, (u8 *)zName, sqlite3Strlen30(zName)+1, &rc);
13764fccf43aSdan 
13774fccf43aSdan       /* Build and compile a statement to execute: */
13784fccf43aSdan       if( rc==SQLITE_OK ){
1379d7fb7d24Sdan         rc = sessionSelectStmt(
1380a9605b91Sdan             db, pSession->zDb, zName, nCol, azCol, abPK, &pSel);
13814fccf43aSdan       }
13824fccf43aSdan 
13831f34f8ccSdan       nNoop = buf.nBuf;
138412ca0b56Sdan       for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
1385e8d5648eSdan         SessionChange *p;         /* Used to iterate through changes */
1386e8d5648eSdan 
13874fccf43aSdan         for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
1388e5754eecSdan           rc = sessionSelectBind(pSel, nCol, abPK, p);
13891f34f8ccSdan           if( sqlite3_step(pSel)==SQLITE_ROW ){
13904fccf43aSdan             int iCol;
1391e8d5648eSdan             if( p->bInsert ){
13924fccf43aSdan               sessionAppendByte(&buf, SQLITE_INSERT, &rc);
1393b4480e94Sdan               sessionAppendByte(&buf, p->bIndirect, &rc);
1394e8d5648eSdan               for(iCol=0; iCol<nCol; iCol++){
13951f34f8ccSdan                 sessionAppendCol(&buf, pSel, iCol, &rc);
13964fccf43aSdan               }
1397e8d5648eSdan             }else{
13981f34f8ccSdan               sessionAppendUpdate(&buf, pSel, p, abPK, &rc);
13994fccf43aSdan             }
1400e8d5648eSdan           }else if( !p->bInsert ){
14014fccf43aSdan             /* A DELETE change */
14024fccf43aSdan             sessionAppendByte(&buf, SQLITE_DELETE, &rc);
1403b4480e94Sdan             sessionAppendByte(&buf, p->bIndirect, &rc);
14044fccf43aSdan             sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
14054fccf43aSdan           }
140612ca0b56Sdan           if( rc==SQLITE_OK ){
14071f34f8ccSdan             rc = sqlite3_reset(pSel);
14084fccf43aSdan           }
14094fccf43aSdan         }
1410e8d5648eSdan       }
14114fccf43aSdan 
14121f34f8ccSdan       sqlite3_finalize(pSel);
14131f34f8ccSdan       if( buf.nBuf==nNoop ){
14144fccf43aSdan         buf.nBuf = nRewind;
14154fccf43aSdan       }
1416*cfdbde21Sdrh       sqlite3_free((char*)azCol);  /* cast works around VC++ bug */
14174fccf43aSdan     }
14184fccf43aSdan   }
14194fccf43aSdan 
14204fccf43aSdan   if( rc==SQLITE_OK ){
14214fccf43aSdan     *pnChangeset = buf.nBuf;
14224fccf43aSdan     *ppChangeset = buf.aBuf;
14234fccf43aSdan   }else{
14244fccf43aSdan     sqlite3_free(buf.aBuf);
14254fccf43aSdan   }
14264c220252Sdan 
1427e5754eecSdan   sqlite3_exec(db, "RELEASE changeset", 0, 0, 0);
14284c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
14294fccf43aSdan   return rc;
14304fccf43aSdan }
14314fccf43aSdan 
1432296c7658Sdan /*
1433296c7658Sdan ** Enable or disable the session object passed as the first argument.
1434296c7658Sdan */
14354fccf43aSdan int sqlite3session_enable(sqlite3_session *pSession, int bEnable){
14364c220252Sdan   int ret;
14374c220252Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
1438296c7658Sdan   if( bEnable>=0 ){
1439296c7658Sdan     pSession->bEnable = bEnable;
14404fccf43aSdan   }
14414c220252Sdan   ret = pSession->bEnable;
14424c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
14434c220252Sdan   return ret;
1444296c7658Sdan }
14454fccf43aSdan 
14464fccf43aSdan /*
1447b4480e94Sdan ** Enable or disable the session object passed as the first argument.
1448b4480e94Sdan */
1449b4480e94Sdan int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect){
1450b4480e94Sdan   int ret;
1451b4480e94Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
1452b4480e94Sdan   if( bIndirect>=0 ){
1453b4480e94Sdan     pSession->bIndirect = bIndirect;
1454b4480e94Sdan   }
1455b4480e94Sdan   ret = pSession->bIndirect;
1456b4480e94Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
1457b4480e94Sdan   return ret;
1458b4480e94Sdan }
1459b4480e94Sdan 
1460b4480e94Sdan /*
14614fccf43aSdan ** Create an iterator used to iterate through the contents of a changeset.
14624fccf43aSdan */
14634fccf43aSdan int sqlite3changeset_start(
1464296c7658Sdan   sqlite3_changeset_iter **pp,    /* OUT: Changeset iterator handle */
1465296c7658Sdan   int nChangeset,                 /* Size of buffer pChangeset in bytes */
1466296c7658Sdan   void *pChangeset                /* Pointer to buffer containing changeset */
14674fccf43aSdan ){
14684fccf43aSdan   sqlite3_changeset_iter *pRet;   /* Iterator to return */
14694fccf43aSdan   int nByte;                      /* Number of bytes to allocate for iterator */
14704fccf43aSdan 
1471296c7658Sdan   /* Zero the output variable in case an error occurs. */
1472296c7658Sdan   *pp = 0;
14734fccf43aSdan 
1474296c7658Sdan   /* Allocate and initialize the iterator structure. */
14754fccf43aSdan   nByte = sizeof(sqlite3_changeset_iter);
14764fccf43aSdan   pRet = (sqlite3_changeset_iter *)sqlite3_malloc(nByte);
14774fccf43aSdan   if( !pRet ) return SQLITE_NOMEM;
14784fccf43aSdan   memset(pRet, 0, sizeof(sqlite3_changeset_iter));
14794fccf43aSdan   pRet->aChangeset = (u8 *)pChangeset;
14804fccf43aSdan   pRet->nChangeset = nChangeset;
14814fccf43aSdan   pRet->pNext = pRet->aChangeset;
14824fccf43aSdan 
1483296c7658Sdan   /* Populate the output variable and return success. */
1484296c7658Sdan   *pp = pRet;
14854fccf43aSdan   return SQLITE_OK;
14864fccf43aSdan }
14874fccf43aSdan 
1488296c7658Sdan /*
1489296c7658Sdan ** Deserialize a single record from a buffer in memory. See "RECORD FORMAT"
1490296c7658Sdan ** for details.
1491296c7658Sdan **
1492296c7658Sdan ** When this function is called, *paChange points to the start of the record
1493296c7658Sdan ** to deserialize. Assuming no error occurs, *paChange is set to point to
1494296c7658Sdan ** one byte after the end of the same record before this function returns.
1495296c7658Sdan **
1496296c7658Sdan ** If successful, each element of the apOut[] array (allocated by the caller)
1497296c7658Sdan ** is set to point to an sqlite3_value object containing the value read
1498296c7658Sdan ** from the corresponding position in the record. If that value is not
1499296c7658Sdan ** included in the record (i.e. because the record is part of an UPDATE change
1500296c7658Sdan ** and the field was not modified), the corresponding element of apOut[] is
1501296c7658Sdan ** set to NULL.
1502296c7658Sdan **
1503296c7658Sdan ** It is the responsibility of the caller to free all sqlite_value structures
1504296c7658Sdan ** using sqlite3_free().
1505296c7658Sdan **
1506296c7658Sdan ** If an error occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
1507296c7658Sdan ** The apOut[] array may have been partially populated in this case.
1508296c7658Sdan */
15094fccf43aSdan static int sessionReadRecord(
15104fccf43aSdan   u8 **paChange,                  /* IN/OUT: Pointer to binary record */
15114fccf43aSdan   int nCol,                       /* Number of values in record */
15124fccf43aSdan   sqlite3_value **apOut           /* Write values to this array */
15134fccf43aSdan ){
1514296c7658Sdan   int i;                          /* Used to iterate through columns */
1515296c7658Sdan   u8 *aRec = *paChange;           /* Cursor for the serialized record */
15164fccf43aSdan 
15174fccf43aSdan   for(i=0; i<nCol; i++){
1518296c7658Sdan     int eType = *aRec++;          /* Type of value (SQLITE_NULL, TEXT etc.) */
151991ddd559Sdan     assert( !apOut || apOut[i]==0 );
15204fccf43aSdan     if( eType ){
152191ddd559Sdan       if( apOut ){
15224fccf43aSdan         apOut[i] = sqlite3ValueNew(0);
15234fccf43aSdan         if( !apOut[i] ) return SQLITE_NOMEM;
152491ddd559Sdan       }
15254fccf43aSdan 
15264fccf43aSdan       if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
15274fccf43aSdan         int nByte;
15284fccf43aSdan         int enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0);
15294fccf43aSdan         aRec += sessionVarintGet(aRec, &nByte);
153091ddd559Sdan         if( apOut ){
15314fccf43aSdan           sqlite3ValueSetStr(apOut[i], nByte, aRec, enc, SQLITE_STATIC);
153291ddd559Sdan         }
15334fccf43aSdan         aRec += nByte;
15344fccf43aSdan       }
15354fccf43aSdan       if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
153691ddd559Sdan         if( apOut ){
15374fccf43aSdan           sqlite3_int64 v = sessionGetI64(aRec);
15384fccf43aSdan           if( eType==SQLITE_INTEGER ){
15394fccf43aSdan             sqlite3VdbeMemSetInt64(apOut[i], v);
15404fccf43aSdan           }else{
15414fccf43aSdan             double d;
15424e895da1Sdan             memcpy(&d, &v, 8);
15434fccf43aSdan             sqlite3VdbeMemSetDouble(apOut[i], d);
15444fccf43aSdan           }
15454fccf43aSdan         }
154691ddd559Sdan         aRec += 8;
154791ddd559Sdan       }
15484fccf43aSdan     }
15494fccf43aSdan   }
15504fccf43aSdan 
15514fccf43aSdan   *paChange = aRec;
15524fccf43aSdan   return SQLITE_OK;
15534fccf43aSdan }
15544fccf43aSdan 
15554fccf43aSdan /*
15564fccf43aSdan ** Advance an iterator created by sqlite3changeset_start() to the next
15574fccf43aSdan ** change in the changeset. This function may return SQLITE_ROW, SQLITE_DONE
15584fccf43aSdan ** or SQLITE_CORRUPT.
15594fccf43aSdan **
15604fccf43aSdan ** This function may not be called on iterators passed to a conflict handler
15614fccf43aSdan ** callback by changeset_apply().
15624fccf43aSdan */
15634fccf43aSdan int sqlite3changeset_next(sqlite3_changeset_iter *p){
15644fccf43aSdan   u8 *aChange;
15654fccf43aSdan   int i;
15664fccf43aSdan   u8 c;
15674fccf43aSdan 
1568296c7658Sdan   /* If the iterator is in the error-state, return immediately. */
15694fccf43aSdan   if( p->rc!=SQLITE_OK ) return p->rc;
15704fccf43aSdan 
1571296c7658Sdan   /* Free the current contents of p->apValue[]. */
15724fccf43aSdan   if( p->apValue ){
15734fccf43aSdan     for(i=0; i<p->nCol*2; i++){
15744fccf43aSdan       sqlite3ValueFree(p->apValue[i]);
15754fccf43aSdan     }
15764fccf43aSdan     memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
15774fccf43aSdan   }
15784fccf43aSdan 
15794fccf43aSdan   /* If the iterator is already at the end of the changeset, return DONE. */
15804fccf43aSdan   if( p->pNext>=&p->aChangeset[p->nChangeset] ){
15814fccf43aSdan     return SQLITE_DONE;
15824fccf43aSdan   }
15834fccf43aSdan   aChange = p->pNext;
15844fccf43aSdan 
15854fccf43aSdan   c = *(aChange++);
15864fccf43aSdan   if( c=='T' ){
15874fccf43aSdan     int nByte;                    /* Bytes to allocate for apValue */
15884fccf43aSdan     aChange += sessionVarintGet(aChange, &p->nCol);
1589244593c8Sdan     p->abPK = (u8 *)aChange;
1590244593c8Sdan     aChange += p->nCol;
15914fccf43aSdan     p->zTab = (char *)aChange;
1592*cfdbde21Sdrh     aChange += (sqlite3Strlen30((char *)aChange) + 1);
15934fccf43aSdan     p->op = *(aChange++);
1594b4480e94Sdan     p->bIndirect = *(aChange++);
15954fccf43aSdan     sqlite3_free(p->apValue);
15964fccf43aSdan     nByte = sizeof(sqlite3_value *) * p->nCol * 2;
15974fccf43aSdan     p->apValue = (sqlite3_value **)sqlite3_malloc(nByte);
15984fccf43aSdan     if( !p->apValue ){
15994fccf43aSdan       return (p->rc = SQLITE_NOMEM);
16004fccf43aSdan     }
16014fccf43aSdan     memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
16024fccf43aSdan   }else{
16034fccf43aSdan     p->op = c;
1604b4480e94Sdan     p->bIndirect = *(aChange++);
16054fccf43aSdan   }
16064fccf43aSdan   if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){
16074fccf43aSdan     return (p->rc = SQLITE_CORRUPT);
16084fccf43aSdan   }
16094fccf43aSdan 
16104fccf43aSdan   /* If this is an UPDATE or DELETE, read the old.* record. */
16114fccf43aSdan   if( p->op!=SQLITE_INSERT ){
16124fccf43aSdan     p->rc = sessionReadRecord(&aChange, p->nCol, p->apValue);
16134fccf43aSdan     if( p->rc!=SQLITE_OK ) return p->rc;
16144fccf43aSdan   }
16154fccf43aSdan 
16164fccf43aSdan   /* If this is an INSERT or UPDATE, read the new.* record. */
16174fccf43aSdan   if( p->op!=SQLITE_DELETE ){
16184fccf43aSdan     p->rc = sessionReadRecord(&aChange, p->nCol, &p->apValue[p->nCol]);
16194fccf43aSdan     if( p->rc!=SQLITE_OK ) return p->rc;
16204fccf43aSdan   }
16214fccf43aSdan 
16224fccf43aSdan   p->pNext = aChange;
16234fccf43aSdan   return SQLITE_ROW;
16244fccf43aSdan }
16254fccf43aSdan 
16264fccf43aSdan /*
1627244593c8Sdan ** The following function extracts information on the current change
16284fccf43aSdan ** from a changeset iterator. They may only be called after changeset_next()
16294fccf43aSdan ** has returned SQLITE_ROW.
16304fccf43aSdan */
16314fccf43aSdan int sqlite3changeset_op(
1632296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Iterator handle */
16334fccf43aSdan   const char **pzTab,             /* OUT: Pointer to table name */
16344fccf43aSdan   int *pnCol,                     /* OUT: Number of columns in table */
1635b4480e94Sdan   int *pOp,                       /* OUT: SQLITE_INSERT, DELETE or UPDATE */
1636b4480e94Sdan   int *pbIndirect                 /* OUT: True if change is indirect */
16374fccf43aSdan ){
16384fccf43aSdan   *pOp = pIter->op;
16394fccf43aSdan   *pnCol = pIter->nCol;
16404fccf43aSdan   *pzTab = pIter->zTab;
1641b4480e94Sdan   if( pbIndirect ) *pbIndirect = pIter->bIndirect;
16424fccf43aSdan   return SQLITE_OK;
16434fccf43aSdan }
16444fccf43aSdan 
1645244593c8Sdan int sqlite3changeset_pk(
1646244593c8Sdan   sqlite3_changeset_iter *pIter,  /* Iterator object */
1647244593c8Sdan   unsigned char **pabPK,          /* OUT: Array of boolean - true for PK cols */
1648244593c8Sdan   int *pnCol                      /* OUT: Number of entries in output array */
1649244593c8Sdan ){
1650244593c8Sdan   *pabPK = pIter->abPK;
1651244593c8Sdan   if( pnCol ) *pnCol = pIter->nCol;
1652244593c8Sdan   return SQLITE_OK;
1653244593c8Sdan }
1654244593c8Sdan 
1655296c7658Sdan /*
1656296c7658Sdan ** This function may only be called while the iterator is pointing to an
1657296c7658Sdan ** SQLITE_UPDATE or SQLITE_DELETE change (see sqlite3changeset_op()).
1658296c7658Sdan ** Otherwise, SQLITE_MISUSE is returned.
1659296c7658Sdan **
1660296c7658Sdan ** It sets *ppValue to point to an sqlite3_value structure containing the
1661296c7658Sdan ** iVal'th value in the old.* record. Or, if that particular value is not
1662296c7658Sdan ** included in the record (because the change is an UPDATE and the field
1663296c7658Sdan ** was not modified and is not a PK column), set *ppValue to NULL.
1664296c7658Sdan **
1665296c7658Sdan ** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is
1666296c7658Sdan ** not modified. Otherwise, SQLITE_OK.
1667296c7658Sdan */
16684fccf43aSdan int sqlite3changeset_old(
1669296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
1670296c7658Sdan   int iVal,                       /* Index of old.* value to retrieve */
16714fccf43aSdan   sqlite3_value **ppValue         /* OUT: Old value (or NULL pointer) */
16724fccf43aSdan ){
1673d5f0767cSdan   if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_DELETE ){
1674d5f0767cSdan     return SQLITE_MISUSE;
1675d5f0767cSdan   }
16764fccf43aSdan   if( iVal<0 || iVal>=pIter->nCol ){
16774fccf43aSdan     return SQLITE_RANGE;
16784fccf43aSdan   }
16794fccf43aSdan   *ppValue = pIter->apValue[iVal];
16804fccf43aSdan   return SQLITE_OK;
16814fccf43aSdan }
16824fccf43aSdan 
1683296c7658Sdan /*
1684296c7658Sdan ** This function may only be called while the iterator is pointing to an
1685296c7658Sdan ** SQLITE_UPDATE or SQLITE_INSERT change (see sqlite3changeset_op()).
1686296c7658Sdan ** Otherwise, SQLITE_MISUSE is returned.
1687296c7658Sdan **
1688296c7658Sdan ** It sets *ppValue to point to an sqlite3_value structure containing the
1689296c7658Sdan ** iVal'th value in the new.* record. Or, if that particular value is not
1690296c7658Sdan ** included in the record (because the change is an UPDATE and the field
1691296c7658Sdan ** was not modified), set *ppValue to NULL.
1692296c7658Sdan **
1693296c7658Sdan ** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is
1694296c7658Sdan ** not modified. Otherwise, SQLITE_OK.
1695296c7658Sdan */
16964fccf43aSdan int sqlite3changeset_new(
1697296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
1698296c7658Sdan   int iVal,                       /* Index of new.* value to retrieve */
16994fccf43aSdan   sqlite3_value **ppValue         /* OUT: New value (or NULL pointer) */
17004fccf43aSdan ){
1701d5f0767cSdan   if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_INSERT ){
1702d5f0767cSdan     return SQLITE_MISUSE;
1703d5f0767cSdan   }
17044fccf43aSdan   if( iVal<0 || iVal>=pIter->nCol ){
17054fccf43aSdan     return SQLITE_RANGE;
17064fccf43aSdan   }
17074fccf43aSdan   *ppValue = pIter->apValue[pIter->nCol+iVal];
17084fccf43aSdan   return SQLITE_OK;
17094fccf43aSdan }
17104fccf43aSdan 
1711296c7658Sdan /*
17127aa469cdSdan ** The following two macros are used internally. They are similar to the
17137aa469cdSdan ** sqlite3changeset_new() and sqlite3changeset_old() functions, except that
17147aa469cdSdan ** they omit all error checking and return a pointer to the requested value.
17157aa469cdSdan */
17167aa469cdSdan #define sessionChangesetNew(pIter, iVal) (pIter)->apValue[(pIter)->nCol+(iVal)]
17177aa469cdSdan #define sessionChangesetOld(pIter, iVal) (pIter)->apValue[(iVal)]
17187aa469cdSdan 
17197aa469cdSdan /*
1720296c7658Sdan ** This function may only be called with a changeset iterator that has been
1721296c7658Sdan ** passed to an SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT
1722296c7658Sdan ** conflict-handler function. Otherwise, SQLITE_MISUSE is returned.
1723296c7658Sdan **
1724296c7658Sdan ** If successful, *ppValue is set to point to an sqlite3_value structure
1725296c7658Sdan ** containing the iVal'th value of the conflicting record.
1726296c7658Sdan **
1727296c7658Sdan ** If value iVal is out-of-range or some other error occurs, an SQLite error
1728296c7658Sdan ** code is returned. Otherwise, SQLITE_OK.
1729296c7658Sdan */
1730d5f0767cSdan int sqlite3changeset_conflict(
1731296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
1732296c7658Sdan   int iVal,                       /* Index of conflict record value to fetch */
1733d5f0767cSdan   sqlite3_value **ppValue         /* OUT: Value from conflicting row */
1734d5f0767cSdan ){
1735d5f0767cSdan   if( !pIter->pConflict ){
1736d5f0767cSdan     return SQLITE_MISUSE;
1737d5f0767cSdan   }
1738d5f0767cSdan   if( iVal<0 || iVal>=sqlite3_column_count(pIter->pConflict) ){
1739d5f0767cSdan     return SQLITE_RANGE;
1740d5f0767cSdan   }
1741d5f0767cSdan   *ppValue = sqlite3_column_value(pIter->pConflict, iVal);
1742d5f0767cSdan   return SQLITE_OK;
1743d5f0767cSdan }
1744d5f0767cSdan 
17454fccf43aSdan /*
17464fccf43aSdan ** Finalize an iterator allocated with sqlite3changeset_start().
17474fccf43aSdan **
17484fccf43aSdan ** This function may not be called on iterators passed to a conflict handler
17494fccf43aSdan ** callback by changeset_apply().
17504fccf43aSdan */
17514fccf43aSdan int sqlite3changeset_finalize(sqlite3_changeset_iter *p){
1752296c7658Sdan   int i;                          /* Used to iterate through p->apValue[] */
1753296c7658Sdan   int rc = p->rc;                 /* Return code */
175412ca0b56Sdan   if( p->apValue ){
17554fccf43aSdan     for(i=0; i<p->nCol*2; i++) sqlite3ValueFree(p->apValue[i]);
175612ca0b56Sdan   }
17574fccf43aSdan   sqlite3_free(p->apValue);
17584fccf43aSdan   sqlite3_free(p);
17594fccf43aSdan   return rc;
17604fccf43aSdan }
17614fccf43aSdan 
176291ddd559Sdan /*
176391ddd559Sdan ** Invert a changeset object.
176491ddd559Sdan */
176591ddd559Sdan int sqlite3changeset_invert(
176691ddd559Sdan   int nChangeset,                 /* Number of bytes in input */
176791ddd559Sdan   void *pChangeset,               /* Input changeset */
176891ddd559Sdan   int *pnInverted,                /* OUT: Number of bytes in output changeset */
176991ddd559Sdan   void **ppInverted               /* OUT: Inverse of pChangeset */
177091ddd559Sdan ){
177191ddd559Sdan   u8 *aOut;
177291ddd559Sdan   u8 *aIn;
177391ddd559Sdan   int i;
177491ddd559Sdan   int nCol = 0;
177591ddd559Sdan 
177691ddd559Sdan   /* Zero the output variables in case an error occurs. */
177791ddd559Sdan   *ppInverted = 0;
177891ddd559Sdan   *pnInverted = 0;
177991ddd559Sdan   if( nChangeset==0 ) return SQLITE_OK;
178091ddd559Sdan 
178191ddd559Sdan   aOut = (u8 *)sqlite3_malloc(nChangeset);
178291ddd559Sdan   if( !aOut ) return SQLITE_NOMEM;
178391ddd559Sdan   aIn = (u8 *)pChangeset;
178491ddd559Sdan 
178591ddd559Sdan   i = 0;
178691ddd559Sdan   while( i<nChangeset ){
178791ddd559Sdan     u8 eType = aIn[i];
178891ddd559Sdan     switch( eType ){
178991ddd559Sdan       case 'T': {
1790244593c8Sdan         /* A 'table' record consists of:
1791244593c8Sdan         **
1792244593c8Sdan         **   * A constant 'T' character,
1793244593c8Sdan         **   * Number of columns in said table (a varint),
1794244593c8Sdan         **   * An array of nCol bytes (abPK),
1795244593c8Sdan         **   * A nul-terminated table name.
1796244593c8Sdan         */
179791ddd559Sdan         int nByte = 1 + sessionVarintGet(&aIn[i+1], &nCol);
1798244593c8Sdan         nByte += nCol;
1799*cfdbde21Sdrh         nByte += 1 + sqlite3Strlen30((char *)&aIn[i+nByte]);
180091ddd559Sdan         memcpy(&aOut[i], &aIn[i], nByte);
180191ddd559Sdan         i += nByte;
180291ddd559Sdan         break;
180391ddd559Sdan       }
180491ddd559Sdan 
180591ddd559Sdan       case SQLITE_INSERT:
180691ddd559Sdan       case SQLITE_DELETE: {
180791ddd559Sdan         int nByte;
1808b4480e94Sdan         u8 *aEnd = &aIn[i+2];
180991ddd559Sdan 
181091ddd559Sdan         sessionReadRecord(&aEnd, nCol, 0);
181191ddd559Sdan         aOut[i] = (eType==SQLITE_DELETE ? SQLITE_INSERT : SQLITE_DELETE);
1812b4480e94Sdan         aOut[i+1] = aIn[i+1];
1813*cfdbde21Sdrh         nByte = (int)(aEnd - &aIn[i+2]);
1814b4480e94Sdan         memcpy(&aOut[i+2], &aIn[i+2], nByte);
1815b4480e94Sdan         i += 2 + nByte;
181691ddd559Sdan         break;
181791ddd559Sdan       }
181891ddd559Sdan 
181991ddd559Sdan       case SQLITE_UPDATE: {
182091ddd559Sdan         int nByte1;              /* Size of old.* record in bytes */
182191ddd559Sdan         int nByte2;              /* Size of new.* record in bytes */
1822b4480e94Sdan         u8 *aEnd = &aIn[i+2];
182391ddd559Sdan 
182491ddd559Sdan         sessionReadRecord(&aEnd, nCol, 0);
1825*cfdbde21Sdrh         nByte1 = (int)(aEnd - &aIn[i+2]);
182691ddd559Sdan         sessionReadRecord(&aEnd, nCol, 0);
1827*cfdbde21Sdrh         nByte2 = (int)(aEnd - &aIn[i+2]) - nByte1;
182891ddd559Sdan 
182991ddd559Sdan         aOut[i] = SQLITE_UPDATE;
1830b4480e94Sdan         aOut[i+1] = aIn[i+1];
1831b4480e94Sdan         memcpy(&aOut[i+2], &aIn[i+2+nByte1], nByte2);
1832b4480e94Sdan         memcpy(&aOut[i+2+nByte2], &aIn[i+2], nByte1);
183391ddd559Sdan 
1834b4480e94Sdan         i += 2 + nByte1 + nByte2;
183591ddd559Sdan         break;
183691ddd559Sdan       }
183791ddd559Sdan 
183891ddd559Sdan       default:
183991ddd559Sdan         sqlite3_free(aOut);
184091ddd559Sdan         return SQLITE_CORRUPT;
184191ddd559Sdan     }
184291ddd559Sdan   }
184391ddd559Sdan 
184491ddd559Sdan   *pnInverted = nChangeset;
184591ddd559Sdan   *ppInverted = (void *)aOut;
184691ddd559Sdan   return SQLITE_OK;
184791ddd559Sdan }
184891ddd559Sdan 
18490c698471Sdan typedef struct SessionApplyCtx SessionApplyCtx;
18500c698471Sdan struct SessionApplyCtx {
18510c698471Sdan   sqlite3 *db;
18520c698471Sdan   sqlite3_stmt *pDelete;          /* DELETE statement */
18530c698471Sdan   sqlite3_stmt *pUpdate;          /* DELETE statement */
18540c698471Sdan   sqlite3_stmt *pInsert;          /* INSERT statement */
18550c698471Sdan   sqlite3_stmt *pSelect;          /* SELECT statement */
18560c698471Sdan   int nCol;                       /* Size of azCol[] and abPK[] arrays */
18570c698471Sdan   const char **azCol;             /* Array of column names */
18580c698471Sdan   u8 *abPK;                       /* Boolean array - true if column is in PK */
18590c698471Sdan };
18600c698471Sdan 
1861d5f0767cSdan /*
1862d5f0767cSdan ** Formulate a statement to DELETE a row from database db. Assuming a table
1863d5f0767cSdan ** structure like this:
1864d5f0767cSdan **
1865d5f0767cSdan **     CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
1866d5f0767cSdan **
1867d5f0767cSdan ** The DELETE statement looks like this:
1868d5f0767cSdan **
1869db04571cSdan **     DELETE FROM x WHERE a = :1 AND c = :3 AND (:5 OR b IS :2 AND d IS :4)
1870d5f0767cSdan **
1871d5f0767cSdan ** Variable :5 (nCol+1) is a boolean. It should be set to 0 if we require
1872d5f0767cSdan ** matching b and d values, or 1 otherwise. The second case comes up if the
1873d5f0767cSdan ** conflict handler is invoked with NOTFOUND and returns CHANGESET_REPLACE.
1874296c7658Sdan **
1875296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pDelete is left
1876296c7658Sdan ** pointing to the prepared version of the SQL statement.
1877d5f0767cSdan */
1878d5f0767cSdan static int sessionDeleteRow(
1879d5f0767cSdan   sqlite3 *db,                    /* Database handle */
1880d5f0767cSdan   const char *zTab,               /* Table name */
18810c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
1882d5f0767cSdan ){
1883296c7658Sdan   int i;
1884296c7658Sdan   const char *zSep = "";
1885d5f0767cSdan   int rc = SQLITE_OK;
1886d5f0767cSdan   SessionBuffer buf = {0, 0, 0};
18877cf7df7dSdan   int nPk = 0;
1888d5f0767cSdan 
1889d5f0767cSdan   sessionAppendStr(&buf, "DELETE FROM ", &rc);
1890d5f0767cSdan   sessionAppendIdent(&buf, zTab, &rc);
1891296c7658Sdan   sessionAppendStr(&buf, " WHERE ", &rc);
1892296c7658Sdan 
1893296c7658Sdan   for(i=0; i<p->nCol; i++){
1894296c7658Sdan     if( p->abPK[i] ){
18957cf7df7dSdan       nPk++;
1896296c7658Sdan       sessionAppendStr(&buf, zSep, &rc);
1897296c7658Sdan       sessionAppendIdent(&buf, p->azCol[i], &rc);
1898296c7658Sdan       sessionAppendStr(&buf, " = ?", &rc);
1899296c7658Sdan       sessionAppendInteger(&buf, i+1, &rc);
1900296c7658Sdan       zSep = " AND ";
1901296c7658Sdan     }
1902296c7658Sdan   }
1903296c7658Sdan 
19047cf7df7dSdan   if( nPk<p->nCol ){
1905296c7658Sdan     sessionAppendStr(&buf, " AND (?", &rc);
1906296c7658Sdan     sessionAppendInteger(&buf, p->nCol+1, &rc);
1907296c7658Sdan     sessionAppendStr(&buf, " OR ", &rc);
1908296c7658Sdan 
1909296c7658Sdan     zSep = "";
1910296c7658Sdan     for(i=0; i<p->nCol; i++){
1911296c7658Sdan       if( !p->abPK[i] ){
1912296c7658Sdan         sessionAppendStr(&buf, zSep, &rc);
1913296c7658Sdan         sessionAppendIdent(&buf, p->azCol[i], &rc);
1914296c7658Sdan         sessionAppendStr(&buf, " IS ?", &rc);
1915296c7658Sdan         sessionAppendInteger(&buf, i+1, &rc);
1916296c7658Sdan         zSep = "AND ";
1917296c7658Sdan       }
1918296c7658Sdan     }
1919296c7658Sdan     sessionAppendStr(&buf, ")", &rc);
19207cf7df7dSdan   }
1921d5f0767cSdan 
1922d5f0767cSdan   if( rc==SQLITE_OK ){
19230c698471Sdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pDelete, 0);
1924d5f0767cSdan   }
1925d5f0767cSdan   sqlite3_free(buf.aBuf);
1926d5f0767cSdan 
1927d5f0767cSdan   return rc;
1928d5f0767cSdan }
1929d5f0767cSdan 
1930d5f0767cSdan /*
1931d5f0767cSdan ** Formulate and prepare a statement to UPDATE a row from database db.
1932d5f0767cSdan ** Assuming a table structure like this:
1933d5f0767cSdan **
1934d5f0767cSdan **     CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
1935d5f0767cSdan **
1936d5f0767cSdan ** The UPDATE statement looks like this:
1937d5f0767cSdan **
1938d5f0767cSdan **     UPDATE x SET
1939d5f0767cSdan **     a = CASE WHEN ?2  THEN ?3  ELSE a END,
1940d5f0767cSdan **     b = CASE WHEN ?5  THEN ?6  ELSE a END,
1941d5f0767cSdan **     c = CASE WHEN ?8  THEN ?9  ELSE a END,
1942d5f0767cSdan **     d = CASE WHEN ?11 THEN ?12 ELSE a END
1943d5f0767cSdan **     WHERE a = ?1 AND c = ?7 AND (?13 OR
1944d5f0767cSdan **       (?5==0 OR b IS ?4) AND (?11==0 OR b IS ?10) AND
1945d5f0767cSdan **     )
1946d5f0767cSdan **
1947d5f0767cSdan ** For each column in the table, there are three variables to bind:
1948d5f0767cSdan **
1949d5f0767cSdan **     ?(i*3+1)    The old.* value of the column, if any.
1950d5f0767cSdan **     ?(i*3+2)    A boolean flag indicating that the value is being modified.
1951d5f0767cSdan **     ?(i*3+3)    The new.* value of the column, if any.
1952d5f0767cSdan **
1953d5f0767cSdan ** Also, a boolean flag that, if set to true, causes the statement to update
1954d5f0767cSdan ** a row even if the non-PK values do not match. This is required if the
1955d5f0767cSdan ** conflict-handler is invoked with CHANGESET_DATA and returns
1956d5f0767cSdan ** CHANGESET_REPLACE. This is variable "?(nCol*3+1)".
1957d5f0767cSdan **
1958296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left
1959296c7658Sdan ** pointing to the prepared version of the SQL statement.
1960d5f0767cSdan */
1961d5f0767cSdan static int sessionUpdateRow(
1962d5f0767cSdan   sqlite3 *db,                    /* Database handle */
1963d5f0767cSdan   const char *zTab,               /* Table name */
19640c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
1965d5f0767cSdan ){
1966d5f0767cSdan   int rc = SQLITE_OK;
1967d5f0767cSdan   int i;
1968d5f0767cSdan   const char *zSep = "";
1969d5f0767cSdan   SessionBuffer buf = {0, 0, 0};
1970d5f0767cSdan 
1971d5f0767cSdan   /* Append "UPDATE tbl SET " */
1972d5f0767cSdan   sessionAppendStr(&buf, "UPDATE ", &rc);
1973d5f0767cSdan   sessionAppendIdent(&buf, zTab, &rc);
1974d5f0767cSdan   sessionAppendStr(&buf, " SET ", &rc);
1975d5f0767cSdan 
1976d5f0767cSdan   /* Append the assignments */
19770c698471Sdan   for(i=0; i<p->nCol; i++){
1978d5f0767cSdan     sessionAppendStr(&buf, zSep, &rc);
19790c698471Sdan     sessionAppendIdent(&buf, p->azCol[i], &rc);
1980d5f0767cSdan     sessionAppendStr(&buf, " = CASE WHEN ?", &rc);
1981d5f0767cSdan     sessionAppendInteger(&buf, i*3+2, &rc);
1982d5f0767cSdan     sessionAppendStr(&buf, " THEN ?", &rc);
1983d5f0767cSdan     sessionAppendInteger(&buf, i*3+3, &rc);
1984d5f0767cSdan     sessionAppendStr(&buf, " ELSE ", &rc);
19850c698471Sdan     sessionAppendIdent(&buf, p->azCol[i], &rc);
1986d5f0767cSdan     sessionAppendStr(&buf, " END", &rc);
1987d5f0767cSdan     zSep = ", ";
1988d5f0767cSdan   }
1989d5f0767cSdan 
1990d5f0767cSdan   /* Append the PK part of the WHERE clause */
1991d5f0767cSdan   sessionAppendStr(&buf, " WHERE ", &rc);
19920c698471Sdan   for(i=0; i<p->nCol; i++){
19930c698471Sdan     if( p->abPK[i] ){
19940c698471Sdan       sessionAppendIdent(&buf, p->azCol[i], &rc);
1995d5f0767cSdan       sessionAppendStr(&buf, " = ?", &rc);
1996d5f0767cSdan       sessionAppendInteger(&buf, i*3+1, &rc);
1997d5f0767cSdan       sessionAppendStr(&buf, " AND ", &rc);
1998d5f0767cSdan     }
1999d5f0767cSdan   }
2000d5f0767cSdan 
2001d5f0767cSdan   /* Append the non-PK part of the WHERE clause */
2002d5f0767cSdan   sessionAppendStr(&buf, " (?", &rc);
20030c698471Sdan   sessionAppendInteger(&buf, p->nCol*3+1, &rc);
2004d5f0767cSdan   sessionAppendStr(&buf, " OR 1", &rc);
20050c698471Sdan   for(i=0; i<p->nCol; i++){
20060c698471Sdan     if( !p->abPK[i] ){
2007d5f0767cSdan       sessionAppendStr(&buf, " AND (?", &rc);
2008d5f0767cSdan       sessionAppendInteger(&buf, i*3+2, &rc);
2009d5f0767cSdan       sessionAppendStr(&buf, "=0 OR ", &rc);
20100c698471Sdan       sessionAppendIdent(&buf, p->azCol[i], &rc);
2011d5f0767cSdan       sessionAppendStr(&buf, " IS ?", &rc);
2012d5f0767cSdan       sessionAppendInteger(&buf, i*3+1, &rc);
2013d5f0767cSdan       sessionAppendStr(&buf, ")", &rc);
2014d5f0767cSdan     }
2015d5f0767cSdan   }
2016d5f0767cSdan   sessionAppendStr(&buf, ")", &rc);
2017d5f0767cSdan 
2018d5f0767cSdan   if( rc==SQLITE_OK ){
20190c698471Sdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0);
2020d5f0767cSdan   }
2021d5f0767cSdan   sqlite3_free(buf.aBuf);
2022d5f0767cSdan 
2023d5f0767cSdan   return rc;
2024d5f0767cSdan }
2025d5f0767cSdan 
2026296c7658Sdan /*
2027296c7658Sdan ** Formulate and prepare an SQL statement to query table zTab by primary
2028296c7658Sdan ** key. Assuming the following table structure:
2029296c7658Sdan **
2030296c7658Sdan **     CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
2031296c7658Sdan **
2032296c7658Sdan ** The SELECT statement looks like this:
2033296c7658Sdan **
2034296c7658Sdan **     SELECT * FROM x WHERE a = ?1 AND c = ?3
2035296c7658Sdan **
2036296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pSelect is left
2037296c7658Sdan ** pointing to the prepared version of the SQL statement.
2038296c7658Sdan */
2039d5f0767cSdan static int sessionSelectRow(
2040d5f0767cSdan   sqlite3 *db,                    /* Database handle */
2041d5f0767cSdan   const char *zTab,               /* Table name */
20420c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
2043d5f0767cSdan ){
2044d7fb7d24Sdan   return sessionSelectStmt(
2045d7fb7d24Sdan       db, "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect);
2046d5f0767cSdan }
2047d5f0767cSdan 
2048296c7658Sdan /*
2049296c7658Sdan ** Formulate and prepare an INSERT statement to add a record to table zTab.
2050296c7658Sdan ** For example:
2051296c7658Sdan **
2052296c7658Sdan **     INSERT INTO main."zTab" VALUES(?1, ?2, ?3 ...);
2053296c7658Sdan **
2054296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pInsert is left
2055296c7658Sdan ** pointing to the prepared version of the SQL statement.
2056296c7658Sdan */
20570c698471Sdan static int sessionInsertRow(
20580c698471Sdan   sqlite3 *db,                    /* Database handle */
20590c698471Sdan   const char *zTab,               /* Table name */
20600c698471Sdan   SessionApplyCtx *p              /* Session changeset-apply context */
20610c698471Sdan ){
20620c698471Sdan   int rc = SQLITE_OK;
20630c698471Sdan   int i;
20640c698471Sdan   SessionBuffer buf = {0, 0, 0};
20650c698471Sdan 
20660c698471Sdan   sessionAppendStr(&buf, "INSERT INTO main.", &rc);
20670c698471Sdan   sessionAppendIdent(&buf, zTab, &rc);
20680c698471Sdan   sessionAppendStr(&buf, " VALUES(?", &rc);
20690c698471Sdan   for(i=1; i<p->nCol; i++){
20700c698471Sdan     sessionAppendStr(&buf, ", ?", &rc);
20710c698471Sdan   }
20720c698471Sdan   sessionAppendStr(&buf, ")", &rc);
20730c698471Sdan 
20740c698471Sdan   if( rc==SQLITE_OK ){
20750c698471Sdan     rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pInsert, 0);
20760c698471Sdan   }
20770c698471Sdan   sqlite3_free(buf.aBuf);
20780c698471Sdan   return rc;
20790c698471Sdan }
20800c698471Sdan 
2081296c7658Sdan /*
20827aa469cdSdan ** A wrapper around sqlite3_bind_value() that detects an extra problem.
20837aa469cdSdan ** See comments in the body of this function for details.
20847aa469cdSdan */
20857aa469cdSdan static int sessionBindValue(
20867aa469cdSdan   sqlite3_stmt *pStmt,            /* Statement to bind value to */
20877aa469cdSdan   int i,                          /* Parameter number to bind to */
20887aa469cdSdan   sqlite3_value *pVal             /* Value to bind */
20897aa469cdSdan ){
20907aa469cdSdan   if( (pVal->type==SQLITE_TEXT || pVal->type==SQLITE_BLOB) && pVal->z==0 ){
20917aa469cdSdan     /* This condition occurs when an earlier OOM in a call to
20927aa469cdSdan     ** sqlite3_value_text() or sqlite3_value_blob() (perhaps from within
20937aa469cdSdan     ** a conflict-hanler) has zeroed the pVal->z pointer. Return NOMEM. */
20947aa469cdSdan     return SQLITE_NOMEM;
20957aa469cdSdan   }
20967aa469cdSdan   return sqlite3_bind_value(pStmt, i, pVal);
20977aa469cdSdan }
20987aa469cdSdan 
20997aa469cdSdan /*
2100db04571cSdan ** Iterator pIter must point to an SQLITE_INSERT entry. This function
2101db04571cSdan ** transfers new.* values from the current iterator entry to statement
2102db04571cSdan ** pStmt. The table being inserted into has nCol columns.
2103db04571cSdan **
2104db04571cSdan ** New.* value $i 0 from the iterator is bound to variable ($i+1) of
2105db04571cSdan ** statement pStmt. If parameter abPK is NULL, all values from 0 to (nCol-1)
2106db04571cSdan ** are transfered to the statement. Otherwise, if abPK is not NULL, it points
2107db04571cSdan ** to an array nCol elements in size. In this case only those values for
2108db04571cSdan ** which abPK[$i] is true are read from the iterator and bound to the
2109db04571cSdan ** statement.
2110db04571cSdan **
2111db04571cSdan ** An SQLite error code is returned if an error occurs. Otherwise, SQLITE_OK.
2112db04571cSdan */
21137aa469cdSdan static int sessionBindRow(
2114db04571cSdan   sqlite3_changeset_iter *pIter,  /* Iterator to read values from */
21157aa469cdSdan   int(*xValue)(sqlite3_changeset_iter *, int, sqlite3_value **),
2116db04571cSdan   int nCol,                       /* Number of columns */
2117db04571cSdan   u8 *abPK,                       /* If not NULL, bind only if true */
2118db04571cSdan   sqlite3_stmt *pStmt             /* Bind values to this statement */
2119db04571cSdan ){
2120db04571cSdan   int i;
2121db04571cSdan   int rc = SQLITE_OK;
21227aa469cdSdan 
21237aa469cdSdan   /* Neither sqlite3changeset_old or sqlite3changeset_new can fail if the
21247aa469cdSdan   ** argument iterator points to a suitable entry. Make sure that xValue
21257aa469cdSdan   ** is one of these to guarantee that it is safe to ignore the return
21267aa469cdSdan   ** in the code below. */
21277aa469cdSdan   assert( xValue==sqlite3changeset_old || xValue==sqlite3changeset_new );
21287aa469cdSdan 
2129db04571cSdan   for(i=0; rc==SQLITE_OK && i<nCol; i++){
2130db04571cSdan     if( !abPK || abPK[i] ){
2131db04571cSdan       sqlite3_value *pVal;
21327aa469cdSdan       (void)xValue(pIter, i, &pVal);
21337aa469cdSdan       rc = sessionBindValue(pStmt, i+1, pVal);
2134db04571cSdan     }
2135db04571cSdan   }
2136db04571cSdan   return rc;
2137db04571cSdan }
2138db04571cSdan 
2139db04571cSdan /*
2140296c7658Sdan ** SQL statement pSelect is as generated by the sessionSelectRow() function.
2141296c7658Sdan ** This function binds the primary key values from the change that changeset
2142296c7658Sdan ** iterator pIter points to to the SELECT and attempts to seek to the table
2143296c7658Sdan ** entry. If a row is found, the SELECT statement left pointing at the row
2144296c7658Sdan ** and SQLITE_ROW is returned. Otherwise, if no row is found and no error
2145296c7658Sdan ** has occured, the statement is reset and SQLITE_OK is returned. If an
21467aa469cdSdan ** error occurs, the statement is reset and an SQLite error code is returned.
21477aa469cdSdan **
21487aa469cdSdan ** If this function returns SQLITE_ROW, the caller must eventually reset()
21497aa469cdSdan ** statement pSelect. If any other value is returned, the statement does
21507aa469cdSdan ** not require a reset().
2151296c7658Sdan **
2152296c7658Sdan ** If the iterator currently points to an INSERT record, bind values from the
2153db04571cSdan ** new.* record to the SELECT statement. Or, if it points to a DELETE or
2154db04571cSdan ** UPDATE, bind values from the old.* record.
2155296c7658Sdan */
21560c698471Sdan static int sessionSeekToRow(
215737f133ecSdan   sqlite3 *db,                    /* Database handle */
215837f133ecSdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
215937f133ecSdan   u8 *abPK,                       /* Primary key flags array */
21600c698471Sdan   sqlite3_stmt *pSelect           /* SELECT statement from sessionSelectRow() */
216137f133ecSdan ){
21627aa469cdSdan   int rc;                         /* Return code */
2163296c7658Sdan   int nCol;                       /* Number of columns in table */
2164296c7658Sdan   int op;                         /* Changset operation (SQLITE_UPDATE etc.) */
2165296c7658Sdan   const char *zDummy;             /* Unused */
216637f133ecSdan 
2167b4480e94Sdan   sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
21687aa469cdSdan   rc = sessionBindRow(pIter,
2169db04571cSdan       op==SQLITE_INSERT ? sqlite3changeset_new : sqlite3changeset_old,
2170db04571cSdan       nCol, abPK, pSelect
2171db04571cSdan   );
21720c698471Sdan 
21730c698471Sdan   if( rc==SQLITE_OK ){
21740c698471Sdan     rc = sqlite3_step(pSelect);
21750c698471Sdan     if( rc!=SQLITE_ROW ) rc = sqlite3_reset(pSelect);
21760c698471Sdan   }
21770c698471Sdan 
21780c698471Sdan   return rc;
21790c698471Sdan }
21800c698471Sdan 
2181296c7658Sdan /*
2182296c7658Sdan ** Invoke the conflict handler for the change that the changeset iterator
2183296c7658Sdan ** currently points to.
2184296c7658Sdan **
2185296c7658Sdan ** Argument eType must be either CHANGESET_DATA or CHANGESET_CONFLICT.
2186296c7658Sdan ** If argument pbReplace is NULL, then the type of conflict handler invoked
2187296c7658Sdan ** depends solely on eType, as follows:
2188296c7658Sdan **
2189296c7658Sdan **    eType value                 Value passed to xConflict
2190296c7658Sdan **    -------------------------------------------------
2191296c7658Sdan **    CHANGESET_DATA              CHANGESET_NOTFOUND
2192296c7658Sdan **    CHANGESET_CONFLICT          CHANGESET_CONSTRAINT
2193296c7658Sdan **
2194296c7658Sdan ** Or, if pbReplace is not NULL, then an attempt is made to find an existing
2195296c7658Sdan ** record with the same primary key as the record about to be deleted, updated
2196296c7658Sdan ** or inserted. If such a record can be found, it is available to the conflict
2197296c7658Sdan ** handler as the "conflicting" record. In this case the type of conflict
2198296c7658Sdan ** handler invoked is as follows:
2199296c7658Sdan **
2200296c7658Sdan **    eType value         PK Record found?   Value passed to xConflict
2201296c7658Sdan **    ----------------------------------------------------------------
2202296c7658Sdan **    CHANGESET_DATA      Yes                CHANGESET_DATA
2203296c7658Sdan **    CHANGESET_DATA      No                 CHANGESET_NOTFOUND
2204296c7658Sdan **    CHANGESET_CONFLICT  Yes                CHANGESET_CONFLICT
2205296c7658Sdan **    CHANGESET_CONFLICT  No                 CHANGESET_CONSTRAINT
2206296c7658Sdan **
2207296c7658Sdan ** If pbReplace is not NULL, and a record with a matching PK is found, and
2208296c7658Sdan ** the conflict handler function returns SQLITE_CHANGESET_REPLACE, *pbReplace
2209296c7658Sdan ** is set to non-zero before returning SQLITE_OK.
2210296c7658Sdan **
2211296c7658Sdan ** If the conflict handler returns SQLITE_CHANGESET_ABORT, SQLITE_ABORT is
2212296c7658Sdan ** returned. Or, if the conflict handler returns an invalid value,
2213296c7658Sdan ** SQLITE_MISUSE. If the conflict handler returns SQLITE_CHANGESET_OMIT,
2214296c7658Sdan ** this function returns SQLITE_OK.
2215296c7658Sdan */
22160c698471Sdan static int sessionConflictHandler(
2217296c7658Sdan   int eType,                      /* Either CHANGESET_DATA or CONFLICT */
2218296c7658Sdan   SessionApplyCtx *p,             /* changeset_apply() context */
22190c698471Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
22200c698471Sdan   int(*xConflict)(void *, int, sqlite3_changeset_iter*),
2221296c7658Sdan   void *pCtx,                     /* First argument for conflict handler */
2222296c7658Sdan   int *pbReplace                  /* OUT: Set to true if PK row is found */
22230c698471Sdan ){
2224296c7658Sdan   int res;                        /* Value returned by conflict handler */
22250c698471Sdan   int rc;
22260c698471Sdan   int nCol;
22270c698471Sdan   int op;
22280c698471Sdan   const char *zDummy;
22290c698471Sdan 
2230b4480e94Sdan   sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
22310c698471Sdan 
22320c698471Sdan   assert( eType==SQLITE_CHANGESET_CONFLICT || eType==SQLITE_CHANGESET_DATA );
22330c698471Sdan   assert( SQLITE_CHANGESET_CONFLICT+1==SQLITE_CHANGESET_CONSTRAINT );
22340c698471Sdan   assert( SQLITE_CHANGESET_DATA+1==SQLITE_CHANGESET_NOTFOUND );
223537f133ecSdan 
223637f133ecSdan   /* Bind the new.* PRIMARY KEY values to the SELECT statement. */
22370c698471Sdan   if( pbReplace ){
22380c698471Sdan     rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect);
22390c698471Sdan   }else{
2240db04571cSdan     rc = SQLITE_OK;
22410c698471Sdan   }
22420c698471Sdan 
22430c698471Sdan   if( rc==SQLITE_ROW ){
22440c698471Sdan     /* There exists another row with the new.* primary key. */
22450c698471Sdan     pIter->pConflict = p->pSelect;
22460c698471Sdan     res = xConflict(pCtx, eType, pIter);
22470c698471Sdan     pIter->pConflict = 0;
22480c698471Sdan     rc = sqlite3_reset(p->pSelect);
2249db04571cSdan   }else if( rc==SQLITE_OK ){
22500c698471Sdan     /* No other row with the new.* primary key. */
22510c698471Sdan     res = xConflict(pCtx, eType+1, pIter);
22520c698471Sdan     if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE;
225337f133ecSdan   }
225437f133ecSdan 
225537f133ecSdan   if( rc==SQLITE_OK ){
22560c698471Sdan     switch( res ){
22570c698471Sdan       case SQLITE_CHANGESET_REPLACE:
2258f51e5f6cSdan         assert( pbReplace );
2259f51e5f6cSdan         *pbReplace = 1;
22600c698471Sdan         break;
22610c698471Sdan 
22620c698471Sdan       case SQLITE_CHANGESET_OMIT:
22630c698471Sdan         break;
22640c698471Sdan 
22650c698471Sdan       case SQLITE_CHANGESET_ABORT:
22660c698471Sdan         rc = SQLITE_ABORT;
22670c698471Sdan         break;
22680c698471Sdan 
22690c698471Sdan       default:
22700c698471Sdan         rc = SQLITE_MISUSE;
22710c698471Sdan         break;
22720c698471Sdan     }
22730c698471Sdan   }
22740c698471Sdan 
22750c698471Sdan   return rc;
22760c698471Sdan }
22770c698471Sdan 
2278296c7658Sdan /*
2279296c7658Sdan ** Attempt to apply the change that the iterator passed as the first argument
2280296c7658Sdan ** currently points to to the database. If a conflict is encountered, invoke
2281296c7658Sdan ** the conflict handler callback.
2282296c7658Sdan **
2283296c7658Sdan ** If argument pbRetry is NULL, then ignore any CHANGESET_DATA conflict. If
2284296c7658Sdan ** one is encountered, update or delete the row with the matching primary key
2285296c7658Sdan ** instead. Or, if pbRetry is not NULL and a CHANGESET_DATA conflict occurs,
2286296c7658Sdan ** invoke the conflict handler. If it returns CHANGESET_REPLACE, set *pbRetry
2287296c7658Sdan ** to true before returning. In this case the caller will invoke this function
2288296c7658Sdan ** again, this time with pbRetry set to NULL.
2289296c7658Sdan **
2290296c7658Sdan ** If argument pbReplace is NULL and a CHANGESET_CONFLICT conflict is
2291296c7658Sdan ** encountered invoke the conflict handler with CHANGESET_CONSTRAINT instead.
2292296c7658Sdan ** Or, if pbReplace is not NULL, invoke it with CHANGESET_CONFLICT. If such
2293296c7658Sdan ** an invocation returns SQLITE_CHANGESET_REPLACE, set *pbReplace to true
2294296c7658Sdan ** before retrying. In this case the caller attempts to remove the conflicting
2295296c7658Sdan ** row before invoking this function again, this time with pbReplace set
2296296c7658Sdan ** to NULL.
2297296c7658Sdan **
2298296c7658Sdan ** If any conflict handler returns SQLITE_CHANGESET_ABORT, this function
2299296c7658Sdan ** returns SQLITE_ABORT. Otherwise, if no error occurs, SQLITE_OK is
2300296c7658Sdan ** returned.
2301296c7658Sdan */
23020c698471Sdan static int sessionApplyOneOp(
2303296c7658Sdan   sqlite3_changeset_iter *pIter,  /* Changeset iterator */
2304296c7658Sdan   SessionApplyCtx *p,             /* changeset_apply() context */
23050c698471Sdan   int(*xConflict)(void *, int, sqlite3_changeset_iter *),
2306296c7658Sdan   void *pCtx,                     /* First argument for the conflict handler */
2307296c7658Sdan   int *pbReplace,                 /* OUT: True to remove PK row and retry */
2308296c7658Sdan   int *pbRetry                    /* OUT: True to retry. */
23090c698471Sdan ){
23100c698471Sdan   const char *zDummy;
23110c698471Sdan   int op;
23120c698471Sdan   int nCol;
23130c698471Sdan   int rc = SQLITE_OK;
23140c698471Sdan 
23150c698471Sdan   assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect );
23160c698471Sdan   assert( p->azCol && p->abPK );
23170c698471Sdan   assert( !pbReplace || *pbReplace==0 );
23180c698471Sdan 
2319b4480e94Sdan   sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
23200c698471Sdan 
23210c698471Sdan   if( op==SQLITE_DELETE ){
23220c698471Sdan 
23230c698471Sdan     /* Bind values to the DELETE statement. */
23247aa469cdSdan     rc = sessionBindRow(pIter, sqlite3changeset_old, nCol, 0, p->pDelete);
23257cf7df7dSdan     if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){
23267cf7df7dSdan       rc = sqlite3_bind_int(p->pDelete, nCol+1, pbRetry==0);
23277cf7df7dSdan     }
23280c698471Sdan     if( rc!=SQLITE_OK ) return rc;
23290c698471Sdan 
23300c698471Sdan     sqlite3_step(p->pDelete);
23310c698471Sdan     rc = sqlite3_reset(p->pDelete);
23320c698471Sdan     if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
23330c698471Sdan       rc = sessionConflictHandler(
23340c698471Sdan           SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
23350c698471Sdan       );
23360c698471Sdan     }else if( rc==SQLITE_CONSTRAINT ){
23370c698471Sdan       rc = sessionConflictHandler(
23380c698471Sdan           SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
23390c698471Sdan       );
23400c698471Sdan     }
23410c698471Sdan 
23420c698471Sdan   }else if( op==SQLITE_UPDATE ){
23430c698471Sdan     int i;
23440c698471Sdan 
23450c698471Sdan     /* Bind values to the UPDATE statement. */
23460c698471Sdan     for(i=0; rc==SQLITE_OK && i<nCol; i++){
23477aa469cdSdan       sqlite3_value *pOld = sessionChangesetOld(pIter, i);
23487aa469cdSdan       sqlite3_value *pNew = sessionChangesetNew(pIter, i);
23497aa469cdSdan 
23500c698471Sdan       sqlite3_bind_int(p->pUpdate, i*3+2, !!pNew);
23517aa469cdSdan       if( pOld ){
23527aa469cdSdan         rc = sessionBindValue(p->pUpdate, i*3+1, pOld);
23537aa469cdSdan       }
23547aa469cdSdan       if( rc==SQLITE_OK && pNew ){
23557aa469cdSdan         rc = sessionBindValue(p->pUpdate, i*3+3, pNew);
23560c698471Sdan       }
23570c698471Sdan     }
23587aa469cdSdan     if( rc==SQLITE_OK ) sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0);
23590c698471Sdan     if( rc!=SQLITE_OK ) return rc;
23600c698471Sdan 
23610c698471Sdan     /* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict,
23620c698471Sdan     ** the result will be SQLITE_OK with 0 rows modified. */
23630c698471Sdan     sqlite3_step(p->pUpdate);
23640c698471Sdan     rc = sqlite3_reset(p->pUpdate);
23650c698471Sdan 
23660c698471Sdan     if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
23670c698471Sdan       /* A NOTFOUND or DATA error. Search the table to see if it contains
23680c698471Sdan       ** a row with a matching primary key. If so, this is a DATA conflict.
23690c698471Sdan       ** Otherwise, if there is no primary key match, it is a NOTFOUND. */
23700c698471Sdan 
23710c698471Sdan       rc = sessionConflictHandler(
23720c698471Sdan           SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
23730c698471Sdan       );
23740c698471Sdan 
23750c698471Sdan     }else if( rc==SQLITE_CONSTRAINT ){
2376db04571cSdan       /* This is always a CONSTRAINT conflict. */
2377db04571cSdan       rc = sessionConflictHandler(
2378db04571cSdan           SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
23790c698471Sdan       );
23800c698471Sdan     }
23810c698471Sdan 
23820c698471Sdan   }else{
23830c698471Sdan     assert( op==SQLITE_INSERT );
23847aa469cdSdan     rc = sessionBindRow(pIter, sqlite3changeset_new, nCol, 0, p->pInsert);
23850c698471Sdan     if( rc!=SQLITE_OK ) return rc;
23860c698471Sdan 
23870c698471Sdan     sqlite3_step(p->pInsert);
23880c698471Sdan     rc = sqlite3_reset(p->pInsert);
2389db04571cSdan     if( rc==SQLITE_CONSTRAINT ){
23900c698471Sdan       rc = sessionConflictHandler(
23910c698471Sdan           SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, pbReplace
23920c698471Sdan       );
239337f133ecSdan     }
239437f133ecSdan   }
239537f133ecSdan 
239637f133ecSdan   return rc;
239737f133ecSdan }
239837f133ecSdan 
2399296c7658Sdan /*
2400296c7658Sdan ** Apply the changeset passed via pChangeset/nChangeset to the main database
2401296c7658Sdan ** attached to handle "db". Invoke the supplied conflict handler callback
2402296c7658Sdan ** to resolve any conflicts encountered while applying the change.
2403296c7658Sdan */
2404d5f0767cSdan int sqlite3changeset_apply(
2405296c7658Sdan   sqlite3 *db,                    /* Apply change to "main" db of this handle */
2406296c7658Sdan   int nChangeset,                 /* Size of changeset in bytes */
2407296c7658Sdan   void *pChangeset,               /* Changeset blob */
2408d5f0767cSdan   int(*xConflict)(
2409d5f0767cSdan     void *pCtx,                   /* Copy of fifth arg to _apply() */
2410d5f0767cSdan     int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
2411d5f0767cSdan     sqlite3_changeset_iter *p     /* Handle describing change and conflict */
2412d5f0767cSdan   ),
2413296c7658Sdan   void *pCtx                      /* First argument passed to xConflict */
2414d5f0767cSdan ){
2415ca62ad57Sdan   int schemaMismatch = 0;
2416296c7658Sdan   sqlite3_changeset_iter *pIter;  /* Iterator to skip through changeset */
2417296c7658Sdan   int rc;                         /* Return code */
2418d5f0767cSdan   const char *zTab = 0;           /* Name of current table */
2419*cfdbde21Sdrh   int nTab = 0;                   /* Result of sqlite3Strlen30(zTab) */
2420296c7658Sdan   SessionApplyCtx sApply;         /* changeset_apply() context object */
2421d5f0767cSdan 
24220c698471Sdan   memset(&sApply, 0, sizeof(sApply));
242312ca0b56Sdan   rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
242412ca0b56Sdan   if( rc!=SQLITE_OK ) return rc;
24250c698471Sdan 
24264c220252Sdan   sqlite3_mutex_enter(sqlite3_db_mutex(db));
24270c698471Sdan   rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
24280c698471Sdan   while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter) ){
24290c698471Sdan     int nCol;
2430d5f0767cSdan     int op;
24310c698471Sdan     int bReplace = 0;
24320c698471Sdan     int bRetry = 0;
24330c698471Sdan     const char *zNew;
2434ca62ad57Sdan 
2435b4480e94Sdan     sqlite3changeset_op(pIter, &zNew, &nCol, &op, 0);
2436d5f0767cSdan 
24370c698471Sdan     if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){
2438ca62ad57Sdan       u8 *abPK;
2439ca62ad57Sdan 
2440ca62ad57Sdan       schemaMismatch = 0;
2441*cfdbde21Sdrh       sqlite3_free((char*)sApply.azCol);  /* cast works around VC++ bug */
24420c698471Sdan       sqlite3_finalize(sApply.pDelete);
24430c698471Sdan       sqlite3_finalize(sApply.pUpdate);
24440c698471Sdan       sqlite3_finalize(sApply.pInsert);
24450c698471Sdan       sqlite3_finalize(sApply.pSelect);
24460c698471Sdan       memset(&sApply, 0, sizeof(sApply));
24470c698471Sdan       sApply.db = db;
244837f133ecSdan 
2449ca62ad57Sdan       sqlite3changeset_pk(pIter, &abPK, 0);
2450296c7658Sdan       rc = sessionTableInfo(
2451ca62ad57Sdan           db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
2452ca62ad57Sdan       );
2453ca62ad57Sdan       if( rc!=SQLITE_OK ) break;
24540c698471Sdan 
2455ca62ad57Sdan       if( sApply.nCol==0 ){
2456ca62ad57Sdan         schemaMismatch = 1;
2457ca62ad57Sdan         sqlite3_log(SQLITE_SCHEMA,
2458ca62ad57Sdan             "sqlite3changeset_apply(): no such table: %s", zTab
2459ca62ad57Sdan         );
2460ca62ad57Sdan       }
2461ca62ad57Sdan       else if( sApply.nCol!=nCol ){
2462ca62ad57Sdan         schemaMismatch = 1;
2463ca62ad57Sdan         sqlite3_log(SQLITE_SCHEMA,
2464ca62ad57Sdan             "sqlite3changeset_apply(): table %s has %d columns, expected %d",
2465ca62ad57Sdan             zTab, sApply.nCol, nCol
2466ca62ad57Sdan         );
2467ca62ad57Sdan       }
2468ca62ad57Sdan       else if( memcmp(sApply.abPK, abPK, nCol)!=0 ){
2469ca62ad57Sdan         schemaMismatch = 1;
2470ca62ad57Sdan         sqlite3_log(SQLITE_SCHEMA,
2471ca62ad57Sdan             "sqlite3changeset_apply(): primary key mismatch for table %s", zTab
2472ca62ad57Sdan         );
2473ca62ad57Sdan       }
2474ca62ad57Sdan       else if(
2475ca62ad57Sdan           (rc = sessionSelectRow(db, zTab, &sApply))
24760c698471Sdan        || (rc = sessionUpdateRow(db, zTab, &sApply))
24770c698471Sdan        || (rc = sessionDeleteRow(db, zTab, &sApply))
24780c698471Sdan        || (rc = sessionInsertRow(db, zTab, &sApply))
247937f133ecSdan       ){
248037f133ecSdan         break;
248137f133ecSdan       }
2482*cfdbde21Sdrh       nTab = sqlite3Strlen30(zTab);
2483d5f0767cSdan     }
2484d5f0767cSdan 
2485ca62ad57Sdan     /* If there is a schema mismatch on the current table, proceed to the
2486ca62ad57Sdan     ** next change. A log message has already been issued. */
2487ca62ad57Sdan     if( schemaMismatch ) continue;
2488ca62ad57Sdan 
24890c698471Sdan     rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, &bReplace, &bRetry);
24900c698471Sdan 
24910c698471Sdan     if( rc==SQLITE_OK && bRetry ){
24920c698471Sdan       rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, &bReplace, 0);
24930c698471Sdan     }
24940c698471Sdan 
24950c698471Sdan     if( bReplace ){
2496db04571cSdan       assert( pIter->op==SQLITE_INSERT );
24970c698471Sdan       rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
24980c698471Sdan       if( rc==SQLITE_OK ){
24997aa469cdSdan         rc = sessionBindRow(pIter,
2500db04571cSdan             sqlite3changeset_new, sApply.nCol, sApply.abPK, sApply.pDelete);
25010c698471Sdan         sqlite3_bind_int(sApply.pDelete, sApply.nCol+1, 1);
25020c698471Sdan       }
25030c698471Sdan       if( rc==SQLITE_OK ){
25040c698471Sdan         sqlite3_step(sApply.pDelete);
25050c698471Sdan         rc = sqlite3_reset(sApply.pDelete);
25060c698471Sdan       }
25070c698471Sdan       if( rc==SQLITE_OK ){
25080c698471Sdan         rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, 0, 0);
25090c698471Sdan       }
25100c698471Sdan       if( rc==SQLITE_OK ){
25110c698471Sdan         rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0);
25120c698471Sdan       }
25130c698471Sdan     }
25140c698471Sdan   }
2515d5f0767cSdan 
2516296c7658Sdan   if( rc==SQLITE_OK ){
2517296c7658Sdan     rc = sqlite3changeset_finalize(pIter);
2518296c7658Sdan   }else{
2519296c7658Sdan     sqlite3changeset_finalize(pIter);
2520296c7658Sdan   }
2521d5f0767cSdan 
2522d5f0767cSdan   if( rc==SQLITE_OK ){
2523d5f0767cSdan     rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
2524d5f0767cSdan   }else{
2525d5f0767cSdan     sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
2526d5f0767cSdan     sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
2527d5f0767cSdan   }
2528d5f0767cSdan 
25290c698471Sdan   sqlite3_finalize(sApply.pInsert);
25300c698471Sdan   sqlite3_finalize(sApply.pDelete);
25310c698471Sdan   sqlite3_finalize(sApply.pUpdate);
25320c698471Sdan   sqlite3_finalize(sApply.pSelect);
2533*cfdbde21Sdrh   sqlite3_free((char*)sApply.azCol);  /* cast works around VC++ bug */
25344c220252Sdan   sqlite3_mutex_leave(sqlite3_db_mutex(db));
2535d5f0767cSdan   return rc;
2536d5f0767cSdan }
253791ddd559Sdan 
25389b1c62d4Sdrh #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
2539