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