14fccf43aSdan 24fccf43aSdan #ifdef SQLITE_ENABLE_SESSION 34fccf43aSdan 44fccf43aSdan #include "sqlite3session.h" 54fccf43aSdan #include <assert.h> 64fccf43aSdan #include <string.h> 74fccf43aSdan 84fccf43aSdan #include "sqliteInt.h" 94fccf43aSdan #include "vdbeInt.h" 104fccf43aSdan 114fccf43aSdan typedef struct SessionTable SessionTable; 124fccf43aSdan typedef struct SessionChange SessionChange; 13296c7658Sdan typedef struct SessionBuffer SessionBuffer; 144fccf43aSdan 15296c7658Sdan /* 16296c7658Sdan ** Session handle structure. 17296c7658Sdan */ 184fccf43aSdan struct sqlite3_session { 194fccf43aSdan sqlite3 *db; /* Database handle session is attached to */ 204fccf43aSdan char *zDb; /* Name of database session is attached to */ 21296c7658Sdan int bEnable; /* True if currently recording */ 224fccf43aSdan int rc; /* Non-zero if an error has occurred */ 234fccf43aSdan sqlite3_session *pNext; /* Next session object on same db. */ 244fccf43aSdan SessionTable *pTable; /* List of attached tables */ 254fccf43aSdan }; 264fccf43aSdan 274fccf43aSdan /* 28296c7658Sdan ** Structure for changeset iterators. 29296c7658Sdan */ 30296c7658Sdan struct sqlite3_changeset_iter { 31296c7658Sdan u8 *aChangeset; /* Pointer to buffer containing changeset */ 32296c7658Sdan int nChangeset; /* Number of bytes in aChangeset */ 33296c7658Sdan u8 *pNext; /* Pointer to next change within aChangeset */ 34296c7658Sdan int rc; /* Iterator error code */ 35296c7658Sdan sqlite3_stmt *pConflict; /* Points to conflicting row, if any */ 36296c7658Sdan char *zTab; /* Current table */ 37296c7658Sdan int nCol; /* Number of columns in zTab */ 38296c7658Sdan int op; /* Current operation */ 39296c7658Sdan sqlite3_value **apValue; /* old.* and new.* values */ 40296c7658Sdan }; 41296c7658Sdan 42296c7658Sdan /* 434fccf43aSdan ** Each session object maintains a set of the following structures, one 444fccf43aSdan ** for each table the session object is monitoring. The structures are 454fccf43aSdan ** stored in a linked list starting at sqlite3_session.pTable. 464fccf43aSdan ** 474fccf43aSdan ** The keys of the SessionTable.aChange[] hash table are all rows that have 484fccf43aSdan ** been modified in any way since the session object was attached to the 494fccf43aSdan ** table. 504fccf43aSdan ** 514fccf43aSdan ** The data associated with each hash-table entry is a structure containing 524fccf43aSdan ** a subset of the initial values that the modified row contained at the 534fccf43aSdan ** start of the session. Or no initial values if the row was inserted. 544fccf43aSdan */ 554fccf43aSdan struct SessionTable { 564fccf43aSdan SessionTable *pNext; 574fccf43aSdan char *zName; /* Local name of table */ 584fccf43aSdan int nCol; /* Number of columns in table zName */ 59e8d5648eSdan const char **azCol; /* Column names */ 60e8d5648eSdan u8 *abPK; /* Array of primary key flags */ 61296c7658Sdan int nEntry; /* Total number of entries in hash table */ 624fccf43aSdan int nChange; /* Size of apChange[] array */ 634fccf43aSdan SessionChange **apChange; /* Hash table buckets */ 644fccf43aSdan }; 654fccf43aSdan 664fccf43aSdan /* 674fccf43aSdan ** RECORD FORMAT: 684fccf43aSdan ** 694fccf43aSdan ** The following record format is similar to (but not compatible with) that 704fccf43aSdan ** used in SQLite database files. This format is used as part of the 714fccf43aSdan ** change-set binary format, and so must be architecture independent. 724fccf43aSdan ** 734fccf43aSdan ** Unlike the SQLite database record format, each field is self-contained - 744fccf43aSdan ** there is no separation of header and data. Each field begins with a 754fccf43aSdan ** single byte describing its type, as follows: 764fccf43aSdan ** 774fccf43aSdan ** 0x00: Undefined value. 784fccf43aSdan ** 0x01: Integer value. 794fccf43aSdan ** 0x02: Real value. 804fccf43aSdan ** 0x03: Text value. 814fccf43aSdan ** 0x04: Blob value. 824fccf43aSdan ** 0x05: SQL NULL value. 834fccf43aSdan ** 844fccf43aSdan ** Note that the above match the definitions of SQLITE_INTEGER, SQLITE_TEXT 854fccf43aSdan ** and so on in sqlite3.h. For undefined and NULL values, the field consists 864fccf43aSdan ** only of the single type byte. For other types of values, the type byte 874fccf43aSdan ** is followed by: 884fccf43aSdan ** 894fccf43aSdan ** Text values: 904fccf43aSdan ** A varint containing the number of bytes in the value (encoded using 914fccf43aSdan ** UTF-8). Followed by a buffer containing the UTF-8 representation 924fccf43aSdan ** of the text value. There is no nul terminator. 934fccf43aSdan ** 944fccf43aSdan ** Blob values: 954fccf43aSdan ** A varint containing the number of bytes in the value, followed by 964fccf43aSdan ** a buffer containing the value itself. 974fccf43aSdan ** 984fccf43aSdan ** Integer values: 994fccf43aSdan ** An 8-byte big-endian integer value. 1004fccf43aSdan ** 1014fccf43aSdan ** Real values: 1024fccf43aSdan ** An 8-byte big-endian IEEE 754-2008 real value. 1034fccf43aSdan ** 1044fccf43aSdan ** Varint values are encoded in the same way as varints in the SQLite 1054fccf43aSdan ** record format. 1064fccf43aSdan ** 1074fccf43aSdan ** CHANGESET FORMAT: 1084fccf43aSdan ** 1094fccf43aSdan ** A changeset is a collection of DELETE, UPDATE and INSERT operations on 1104fccf43aSdan ** one or more tables. Operations on a single table are grouped together, 1114fccf43aSdan ** but may occur in any order (i.e. deletes, updates and inserts are all 1124fccf43aSdan ** mixed together). 1134fccf43aSdan ** 1144fccf43aSdan ** Each group of changes begins with a table header: 1154fccf43aSdan ** 1164fccf43aSdan ** 1 byte: Constant 0x54 (capital 'T') 1174fccf43aSdan ** Varint: Big-endian integer set to the number of columns in the table. 1184fccf43aSdan ** N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated. 1194fccf43aSdan ** 1204fccf43aSdan ** Followed by one or more changes to the table. 1214fccf43aSdan ** 1224fccf43aSdan ** 1 byte: Either SQLITE_INSERT, UPDATE or DELETE. 1234fccf43aSdan ** old.* record: (delete and update only) 1244fccf43aSdan ** new.* record: (insert and update only) 1254fccf43aSdan */ 1264fccf43aSdan 1274fccf43aSdan /* 1284fccf43aSdan ** For each row modified during a session, there exists a single instance of 1294fccf43aSdan ** this structure stored in a SessionTable.aChange[] hash table. 1304fccf43aSdan */ 1314fccf43aSdan struct SessionChange { 132e8d5648eSdan int bInsert; /* True if row was inserted this session */ 1334fccf43aSdan int nRecord; /* Number of bytes in buffer aRecord[] */ 1344fccf43aSdan u8 *aRecord; /* Buffer containing old.* record */ 1354fccf43aSdan SessionChange *pNext; /* For hash-table collisions */ 1364fccf43aSdan }; 1374fccf43aSdan 138296c7658Sdan /* 139296c7658Sdan ** Instances of this structure are used to build strings or binary records. 140296c7658Sdan */ 141296c7658Sdan struct SessionBuffer { 142296c7658Sdan u8 *aBuf; /* Pointer to changeset buffer */ 143296c7658Sdan int nBuf; /* Size of buffer aBuf */ 144296c7658Sdan int nAlloc; /* Size of allocation containing aBuf */ 145296c7658Sdan }; 1464fccf43aSdan 147296c7658Sdan /* 148296c7658Sdan ** Write a varint with value iVal into the buffer at aBuf. Return the 149296c7658Sdan ** number of bytes written. 150296c7658Sdan */ 151296c7658Sdan static int sessionVarintPut(u8 *aBuf, int iVal){ 152296c7658Sdan return putVarint32(aBuf, iVal); 1534fccf43aSdan } 1544fccf43aSdan 155296c7658Sdan /* 156296c7658Sdan ** Return the number of bytes required to store value iVal as a varint. 157296c7658Sdan */ 158296c7658Sdan static int sessionVarintLen(int iVal){ 159296c7658Sdan return sqlite3VarintLen(iVal); 160296c7658Sdan } 161296c7658Sdan 162296c7658Sdan /* 163296c7658Sdan ** Read a varint value from aBuf[] into *piVal. Return the number of 164296c7658Sdan ** bytes read. 165296c7658Sdan */ 1664fccf43aSdan static int sessionVarintGet(u8 *aBuf, int *piVal){ 167296c7658Sdan return getVarint32(aBuf, *piVal); 1684fccf43aSdan } 1694fccf43aSdan 170296c7658Sdan /* 171296c7658Sdan ** Read a 64-bit big-endian integer value from buffer aRec[]. Return 172296c7658Sdan ** the value read. 173296c7658Sdan */ 1744fccf43aSdan static sqlite3_int64 sessionGetI64(u8 *aRec){ 1754fccf43aSdan return (((sqlite3_int64)aRec[0]) << 56) 1764fccf43aSdan + (((sqlite3_int64)aRec[1]) << 48) 1774fccf43aSdan + (((sqlite3_int64)aRec[2]) << 40) 1784fccf43aSdan + (((sqlite3_int64)aRec[3]) << 32) 1794fccf43aSdan + (((sqlite3_int64)aRec[4]) << 24) 1804fccf43aSdan + (((sqlite3_int64)aRec[5]) << 16) 1814fccf43aSdan + (((sqlite3_int64)aRec[6]) << 8) 1824fccf43aSdan + (((sqlite3_int64)aRec[7]) << 0); 1834fccf43aSdan } 1844fccf43aSdan 1854fccf43aSdan /* 186296c7658Sdan ** Write a 64-bit big-endian integer value to the buffer aBuf[]. 187296c7658Sdan */ 188296c7658Sdan static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){ 189296c7658Sdan aBuf[0] = (i>>56) & 0xFF; 190296c7658Sdan aBuf[1] = (i>>48) & 0xFF; 191296c7658Sdan aBuf[2] = (i>>40) & 0xFF; 192296c7658Sdan aBuf[3] = (i>>32) & 0xFF; 193296c7658Sdan aBuf[4] = (i>>24) & 0xFF; 194296c7658Sdan aBuf[5] = (i>>16) & 0xFF; 195296c7658Sdan aBuf[6] = (i>> 8) & 0xFF; 196296c7658Sdan aBuf[7] = (i>> 0) & 0xFF; 197296c7658Sdan } 198296c7658Sdan 199296c7658Sdan /* 2004fccf43aSdan ** This function is used to serialize the contents of value pValue (see 2014fccf43aSdan ** comment titled "RECORD FORMAT" above). 2024fccf43aSdan ** 2034fccf43aSdan ** If it is non-NULL, the serialized form of the value is written to 2044fccf43aSdan ** buffer aBuf. *pnWrite is set to the number of bytes written before 2054fccf43aSdan ** returning. Or, if aBuf is NULL, the only thing this function does is 2064fccf43aSdan ** set *pnWrite. 2074fccf43aSdan ** 2084fccf43aSdan ** If no error occurs, SQLITE_OK is returned. Or, if an OOM error occurs 2094fccf43aSdan ** within a call to sqlite3_value_text() (may fail if the db is utf-16)) 2104fccf43aSdan ** SQLITE_NOMEM is returned. 2114fccf43aSdan */ 2124fccf43aSdan static int sessionSerializeValue( 2134fccf43aSdan u8 *aBuf, /* If non-NULL, write serialized value here */ 2144fccf43aSdan sqlite3_value *pValue, /* Value to serialize */ 2154fccf43aSdan int *pnWrite /* IN/OUT: Increment by bytes written */ 2164fccf43aSdan ){ 217296c7658Sdan int eType; /* Value type (SQLITE_NULL, TEXT etc.) */ 218296c7658Sdan int nByte; /* Size of serialized value in bytes */ 2194fccf43aSdan 2204fccf43aSdan eType = sqlite3_value_type(pValue); 2214fccf43aSdan if( aBuf ) aBuf[0] = eType; 2224fccf43aSdan 2234fccf43aSdan switch( eType ){ 2244fccf43aSdan case SQLITE_NULL: 2254fccf43aSdan nByte = 1; 2264fccf43aSdan break; 2274fccf43aSdan 2284fccf43aSdan case SQLITE_INTEGER: 2294fccf43aSdan case SQLITE_FLOAT: 2304fccf43aSdan if( aBuf ){ 2314fccf43aSdan /* TODO: SQLite does something special to deal with mixed-endian 2324fccf43aSdan ** floating point values (e.g. ARM7). This code probably should 2334fccf43aSdan ** too. */ 2344fccf43aSdan u64 i; 2354fccf43aSdan if( eType==SQLITE_INTEGER ){ 2364fccf43aSdan i = (u64)sqlite3_value_int64(pValue); 2374fccf43aSdan }else{ 2384fccf43aSdan double r; 2394fccf43aSdan assert( sizeof(double)==8 && sizeof(u64)==8 ); 2404fccf43aSdan r = sqlite3_value_double(pValue); 2414fccf43aSdan memcpy(&i, &r, 8); 2424fccf43aSdan } 243296c7658Sdan sessionPutI64(&aBuf[1], i); 2444fccf43aSdan } 2454fccf43aSdan nByte = 9; 2464fccf43aSdan break; 2474fccf43aSdan 2484fccf43aSdan case SQLITE_TEXT: 2494fccf43aSdan case SQLITE_BLOB: { 2504fccf43aSdan int n = sqlite3_value_bytes(pValue); 251296c7658Sdan int nVarint = sessionVarintLen(n); 2524fccf43aSdan if( aBuf ){ 2534fccf43aSdan sessionVarintPut(&aBuf[1], n); 2544fccf43aSdan memcpy(&aBuf[nVarint + 1], eType==SQLITE_TEXT ? 2554fccf43aSdan sqlite3_value_text(pValue) : sqlite3_value_blob(pValue), n 2564fccf43aSdan ); 2574fccf43aSdan } 2584fccf43aSdan 2594fccf43aSdan nByte = 1 + nVarint + n; 2604fccf43aSdan break; 2614fccf43aSdan } 2624fccf43aSdan } 2634fccf43aSdan 2644fccf43aSdan *pnWrite += nByte; 2654fccf43aSdan return SQLITE_OK; 2664fccf43aSdan } 2674fccf43aSdan 268e8d5648eSdan #define HASH_APPEND(hash, add) ((hash) << 3) ^ (hash) ^ (int)(add) 269e8d5648eSdan static int sessionHashAppendI64(int h, i64 i){ 270e8d5648eSdan h = HASH_APPEND(h, i & 0xFFFFFFFF); 271e8d5648eSdan return HASH_APPEND(h, (i>>32)&0xFFFFFFFF); 272e8d5648eSdan } 273e8d5648eSdan static int sessionHashAppendBlob(int h, int n, const u8 *z){ 274e8d5648eSdan int i; 275e8d5648eSdan for(i=0; i<n; i++) h = HASH_APPEND(h, z[i]); 276e8d5648eSdan return h; 277e8d5648eSdan } 278e8d5648eSdan 2794fccf43aSdan /* 280e8d5648eSdan ** This function calculates a hash based on the primary key values of 281e8d5648eSdan ** the old.* or new.* row currently available. 2824fccf43aSdan */ 283e8d5648eSdan static int sessionPreupdateHash( 284e8d5648eSdan sqlite3 *db, /* Database handle */ 285e8d5648eSdan SessionTable *pTab, /* Session table handle */ 286e8d5648eSdan int bNew, /* True to hash the new.* PK */ 287e8d5648eSdan int *piHash /* OUT: Hash value */ 288e8d5648eSdan ){ 289e8d5648eSdan int h = 0; 290e8d5648eSdan int i; 291e8d5648eSdan 292e8d5648eSdan assert( pTab->nCol==sqlite3_preupdate_count(db) ); 293e8d5648eSdan for(i=0; i<pTab->nCol; i++){ 294e8d5648eSdan if( pTab->abPK[i] ){ 295e8d5648eSdan int rc; 296e8d5648eSdan int eType; 297e8d5648eSdan sqlite3_value *pVal; 298e8d5648eSdan 299e8d5648eSdan if( bNew ){ 300e8d5648eSdan rc = sqlite3_preupdate_new(db, i, &pVal); 301e8d5648eSdan }else{ 302e8d5648eSdan rc = sqlite3_preupdate_old(db, i, &pVal); 303e8d5648eSdan } 304e8d5648eSdan 305e8d5648eSdan eType = sqlite3_value_type(pVal); 306e8d5648eSdan h = HASH_APPEND(h, eType); 307e8d5648eSdan switch( eType ){ 308e8d5648eSdan case SQLITE_INTEGER: 309e8d5648eSdan case SQLITE_FLOAT: { 310e8d5648eSdan i64 iVal; 311e8d5648eSdan if( eType==SQLITE_INTEGER ){ 312e8d5648eSdan iVal = sqlite3_value_int64(pVal); 313e8d5648eSdan }else{ 314e8d5648eSdan double rVal = sqlite3_value_double(pVal); 315e8d5648eSdan assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); 316e8d5648eSdan memcpy(&iVal, &rVal, 8); 317e8d5648eSdan } 318e8d5648eSdan h = sessionHashAppendI64(h, iVal); 319e8d5648eSdan break; 320e8d5648eSdan } 321e8d5648eSdan 322e8d5648eSdan case SQLITE_TEXT: 323e8d5648eSdan case SQLITE_BLOB: { 324e8d5648eSdan int n = sqlite3_value_bytes(pVal); 325e8d5648eSdan const u8 *z = eType==SQLITE_TEXT ? 326e8d5648eSdan sqlite3_value_text(pVal) : sqlite3_value_blob(pVal); 327e8d5648eSdan h = sessionHashAppendBlob(h, n, z); 328e8d5648eSdan break; 329e8d5648eSdan } 330e8d5648eSdan } 331e8d5648eSdan } 332e8d5648eSdan } 333e8d5648eSdan 334e8d5648eSdan *piHash = (h % pTab->nChange); 335e8d5648eSdan return SQLITE_OK; 336e8d5648eSdan } 337e8d5648eSdan 338e8d5648eSdan static int sessionChangeHash( 339e8d5648eSdan sqlite3 *db, 340e8d5648eSdan SessionTable *pTab, 341e8d5648eSdan SessionChange *pChange, 342e8d5648eSdan int nBucket 343e8d5648eSdan ){ 344e8d5648eSdan int h = 0; 345e8d5648eSdan int i; 346e8d5648eSdan u8 *a = pChange->aRecord; 347e8d5648eSdan 348e8d5648eSdan for(i=0; i<pTab->nCol; i++){ 349e8d5648eSdan int eType = *a++; 350e8d5648eSdan int isPK = pTab->abPK[i]; 351e8d5648eSdan 352e8d5648eSdan if( isPK ) h = HASH_APPEND(h, eType); 353e8d5648eSdan switch( eType ){ 354e8d5648eSdan case SQLITE_INTEGER: 355e8d5648eSdan case SQLITE_FLOAT: { 356e8d5648eSdan if( isPK ){ 357e8d5648eSdan i64 iVal = sessionGetI64(a); 358e8d5648eSdan h = sessionHashAppendI64(h, iVal); 359e8d5648eSdan } 360e8d5648eSdan a += 8; 361e8d5648eSdan break; 362e8d5648eSdan } 363e8d5648eSdan case SQLITE_TEXT: 364e8d5648eSdan case SQLITE_BLOB: { 365e8d5648eSdan int n; 366e8d5648eSdan a += sessionVarintGet(a, &n); 367e8d5648eSdan if( isPK ){ 368e8d5648eSdan h = sessionHashAppendBlob(h, n, a); 369e8d5648eSdan } 370e8d5648eSdan a += n; 371e8d5648eSdan break; 372e8d5648eSdan } 373e8d5648eSdan } 374e8d5648eSdan } 375e8d5648eSdan return (h % nBucket); 376e8d5648eSdan } 377e8d5648eSdan 378e8d5648eSdan static int sessionPreupdateEqual( 379e8d5648eSdan sqlite3 *db, 380e8d5648eSdan SessionTable *pTab, 381e8d5648eSdan SessionChange *pChange, 382e8d5648eSdan int bNew, 383e8d5648eSdan int *pbEqual 384e8d5648eSdan ){ 385e8d5648eSdan int i; 386e8d5648eSdan u8 *a = pChange->aRecord; 387e8d5648eSdan 388e8d5648eSdan *pbEqual = 0; 389e8d5648eSdan 390e8d5648eSdan for(i=0; i<pTab->nCol; i++){ 391e8d5648eSdan int eType = *a++; 392e8d5648eSdan if( !pTab->abPK[i] ){ 393e8d5648eSdan switch( eType ){ 394e8d5648eSdan case SQLITE_INTEGER: 395e8d5648eSdan case SQLITE_FLOAT: 396e8d5648eSdan a += 8; 397e8d5648eSdan break; 398e8d5648eSdan 399e8d5648eSdan case SQLITE_TEXT: 400e8d5648eSdan case SQLITE_BLOB: { 401e8d5648eSdan int n; 402e8d5648eSdan a += sessionVarintGet(a, &n); 403e8d5648eSdan a += n; 404e8d5648eSdan break; 405e8d5648eSdan } 406e8d5648eSdan } 407e8d5648eSdan }else{ 408e8d5648eSdan sqlite3_value *pVal; 409e8d5648eSdan int rc; 410e8d5648eSdan if( bNew ){ 411e8d5648eSdan rc = sqlite3_preupdate_new(db, i, &pVal); 412e8d5648eSdan }else{ 413e8d5648eSdan rc = sqlite3_preupdate_old(db, i, &pVal); 414e8d5648eSdan } 415e8d5648eSdan if( rc!=SQLITE_OK || sqlite3_value_type(pVal)!=eType ) return rc; 416e8d5648eSdan 417e8d5648eSdan switch( eType ){ 418e8d5648eSdan case SQLITE_INTEGER: 419e8d5648eSdan case SQLITE_FLOAT: { 420e8d5648eSdan i64 iVal = sessionGetI64(a); 421e8d5648eSdan a += 8; 422e8d5648eSdan if( eType==SQLITE_INTEGER ){ 423e8d5648eSdan if( sqlite3_value_int64(pVal)!=iVal ) return SQLITE_OK; 424e8d5648eSdan }else{ 425e8d5648eSdan double rVal; 426e8d5648eSdan assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); 427e8d5648eSdan memcpy(&rVal, &iVal, 8); 428e8d5648eSdan if( sqlite3_value_double(pVal)!=rVal ) return SQLITE_OK; 429e8d5648eSdan } 430e8d5648eSdan break; 431e8d5648eSdan } 432e8d5648eSdan case SQLITE_TEXT: 433e8d5648eSdan case SQLITE_BLOB: { 434e8d5648eSdan int n; 435e8d5648eSdan const u8 *z; 436e8d5648eSdan a += sessionVarintGet(a, &n); 437e8d5648eSdan if( sqlite3_value_bytes(pVal)!=n ) return SQLITE_OK; 438e8d5648eSdan z = eType==SQLITE_TEXT ? 439e8d5648eSdan sqlite3_value_text(pVal) : sqlite3_value_blob(pVal); 440e8d5648eSdan if( memcmp(a, z, n) ) return SQLITE_OK; 441e8d5648eSdan a += n; 442e8d5648eSdan break; 443e8d5648eSdan } 444e8d5648eSdan } 445e8d5648eSdan } 446e8d5648eSdan } 447e8d5648eSdan 448e8d5648eSdan *pbEqual = 1; 449e8d5648eSdan return SQLITE_OK; 4504fccf43aSdan } 4514fccf43aSdan 4524fccf43aSdan /* 4534fccf43aSdan ** If required, grow the hash table used to store changes on table pTab 4544fccf43aSdan ** (part of the session pSession). If a fatal OOM error occurs, set the 4554fccf43aSdan ** session object to failed and return SQLITE_ERROR. Otherwise, return 4564fccf43aSdan ** SQLITE_OK. 4574fccf43aSdan ** 4584fccf43aSdan ** It is possible that a non-fatal OOM error occurs in this function. In 4594fccf43aSdan ** that case the hash-table does not grow, but SQLITE_OK is returned anyway. 4604fccf43aSdan ** Growing the hash table in this case is a performance optimization only, 4614fccf43aSdan ** it is not required for correct operation. 4624fccf43aSdan */ 4634fccf43aSdan static int sessionGrowHash(sqlite3_session *pSession, SessionTable *pTab){ 4644fccf43aSdan if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){ 4654fccf43aSdan int i; 4664fccf43aSdan SessionChange **apNew; 4674fccf43aSdan int nNew = (pTab->nChange ? pTab->nChange : 128) * 2; 4684fccf43aSdan 4694fccf43aSdan apNew = (SessionChange **)sqlite3_malloc(sizeof(SessionChange *) * nNew); 4704fccf43aSdan if( apNew==0 ){ 4714fccf43aSdan if( pTab->nChange==0 ){ 4724fccf43aSdan pSession->rc = SQLITE_NOMEM; 4734fccf43aSdan return SQLITE_ERROR; 4744fccf43aSdan } 4754fccf43aSdan return SQLITE_OK; 4764fccf43aSdan } 4774fccf43aSdan memset(apNew, 0, sizeof(SessionChange *) * nNew); 4784fccf43aSdan 4794fccf43aSdan for(i=0; i<pTab->nChange; i++){ 4804fccf43aSdan SessionChange *p; 4814fccf43aSdan SessionChange *pNext; 4824fccf43aSdan for(p=pTab->apChange[i]; p; p=pNext){ 483e8d5648eSdan int iHash = sessionChangeHash(pSession->db, pTab, p, nNew); 4844fccf43aSdan pNext = p->pNext; 4854fccf43aSdan p->pNext = apNew[iHash]; 4864fccf43aSdan apNew[iHash] = p; 4874fccf43aSdan } 4884fccf43aSdan } 4894fccf43aSdan 4904fccf43aSdan sqlite3_free(pTab->apChange); 4914fccf43aSdan pTab->nChange = nNew; 4924fccf43aSdan pTab->apChange = apNew; 4934fccf43aSdan } 4944fccf43aSdan 4954fccf43aSdan return SQLITE_OK; 4964fccf43aSdan } 4974fccf43aSdan 498296c7658Sdan /* 499e8d5648eSdan ** This function queries the database for the names of the columns of table 500e8d5648eSdan ** zThis, in schema zDb. It is expected that the table has nCol columns. If 501e8d5648eSdan ** not, SQLITE_SCHEMA is returned and none of the output variables are 502e8d5648eSdan ** populated. 503e8d5648eSdan ** 504e8d5648eSdan ** Otherwise, if it is not NULL, variable *pzTab is set to point to a 505e8d5648eSdan ** nul-terminated copy of the table name. *pazCol (if not NULL) is set to 506e8d5648eSdan ** point to an array of pointers to column names. And *pabPK (again, if not 507e8d5648eSdan ** NULL) is set to point to an array of booleans - true if the corresponding 508e8d5648eSdan ** column is part of the primary key. 509e8d5648eSdan ** 510e8d5648eSdan ** For example, if the table is declared as: 511e8d5648eSdan ** 512e8d5648eSdan ** CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z)); 513e8d5648eSdan ** 514e8d5648eSdan ** Then the three output variables are populated as follows: 515e8d5648eSdan ** 516e8d5648eSdan ** *pzTab = "tbl1" 517e8d5648eSdan ** *pazCol = {"w", "x", "y", "z"} 518e8d5648eSdan ** *pabPK = {1, 0, 0, 1} 519e8d5648eSdan ** 520e8d5648eSdan ** All returned buffers are part of the same single allocation, which must 521e8d5648eSdan ** be freed using sqlite3_free() by the caller. If pazCol was not NULL, then 522e8d5648eSdan ** pointer *pazCol should be freed to release all memory. Otherwise, pointer 523e8d5648eSdan ** *pabPK. It is illegal for both pazCol and pabPK to be NULL. 524e8d5648eSdan */ 525e8d5648eSdan static int sessionTableInfo( 526e8d5648eSdan sqlite3 *db, /* Database connection */ 527e8d5648eSdan const char *zDb, /* Name of attached database (e.g. "main") */ 528e8d5648eSdan const char *zThis, /* Table name */ 529e8d5648eSdan int nCol, /* Expected number of columns */ 530e8d5648eSdan const char **pzTab, /* OUT: Copy of zThis */ 531e8d5648eSdan const char ***pazCol, /* OUT: Array of column names for table */ 532e8d5648eSdan u8 **pabPK /* OUT: Array of booleans - true for PK col */ 533e8d5648eSdan ){ 534e8d5648eSdan char *zPragma; 535e8d5648eSdan sqlite3_stmt *pStmt; 536e8d5648eSdan int rc; 537e8d5648eSdan int nByte; 538e8d5648eSdan int nDbCol = 0; 539e8d5648eSdan int nThis; 540e8d5648eSdan int i; 541e8d5648eSdan u8 *pAlloc; 542e8d5648eSdan u8 *pFree = 0; 543e8d5648eSdan char **azCol; 544e8d5648eSdan u8 *abPK; 545e8d5648eSdan 546e8d5648eSdan assert( pazCol || pabPK ); 547e8d5648eSdan 548e8d5648eSdan nThis = strlen(zThis); 549e8d5648eSdan zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis); 550e8d5648eSdan if( !zPragma ) return SQLITE_NOMEM; 551e8d5648eSdan 552e8d5648eSdan rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0); 553e8d5648eSdan sqlite3_free(zPragma); 554e8d5648eSdan if( rc!=SQLITE_OK ) return rc; 555e8d5648eSdan 556e8d5648eSdan nByte = nThis + 1; 557e8d5648eSdan while( SQLITE_ROW==sqlite3_step(pStmt) ){ 558e8d5648eSdan nByte += sqlite3_column_bytes(pStmt, 1); 559e8d5648eSdan nDbCol++; 560e8d5648eSdan } 561e8d5648eSdan rc = sqlite3_reset(pStmt); 562e8d5648eSdan 563e8d5648eSdan if( nDbCol!=nCol ){ 564e8d5648eSdan rc = SQLITE_SCHEMA; 565e8d5648eSdan } 566e8d5648eSdan if( rc==SQLITE_OK ){ 567e8d5648eSdan nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); 568e8d5648eSdan pAlloc = sqlite3_malloc(nByte); 569e8d5648eSdan if( pAlloc==0 ){ 570e8d5648eSdan rc = SQLITE_NOMEM; 571e8d5648eSdan } 572e8d5648eSdan } 573e8d5648eSdan if( rc==SQLITE_OK ){ 574e8d5648eSdan pFree = pAlloc; 575e8d5648eSdan if( pazCol ){ 576e8d5648eSdan azCol = (char **)pAlloc; 577e8d5648eSdan pAlloc = (u8 *)&azCol[nCol]; 578e8d5648eSdan } 579e8d5648eSdan if( pabPK ){ 580e8d5648eSdan abPK = (u8 *)pAlloc; 581e8d5648eSdan pAlloc = &abPK[nCol]; 582e8d5648eSdan } 583e8d5648eSdan if( pzTab ){ 584e8d5648eSdan memcpy(pAlloc, zThis, nThis+1); 585e8d5648eSdan *pzTab = (char *)pAlloc; 586e8d5648eSdan pAlloc += nThis+1; 587e8d5648eSdan } 588e8d5648eSdan 589e8d5648eSdan i = 0; 590e8d5648eSdan while( SQLITE_ROW==sqlite3_step(pStmt) ){ 591e8d5648eSdan int nName = sqlite3_column_bytes(pStmt, 1); 592e8d5648eSdan const unsigned char *zName = sqlite3_column_text(pStmt, 1); 593e8d5648eSdan if( zName==0 ) break; 594e8d5648eSdan if( pazCol ){ 595e8d5648eSdan memcpy(pAlloc, zName, nName+1); 596e8d5648eSdan azCol[i] = (char *)pAlloc; 597e8d5648eSdan pAlloc += nName+1; 598e8d5648eSdan } 599e8d5648eSdan if( pabPK ) abPK[i] = sqlite3_column_int(pStmt, 5); 600e8d5648eSdan i++; 601e8d5648eSdan } 602e8d5648eSdan rc = sqlite3_reset(pStmt); 603e8d5648eSdan 604e8d5648eSdan } 605e8d5648eSdan 606e8d5648eSdan /* If successful, populate the output variables. Otherwise, zero them and 607e8d5648eSdan ** free any allocation made. An error code will be returned in this case. 608e8d5648eSdan */ 609e8d5648eSdan if( rc==SQLITE_OK ){ 610e8d5648eSdan if( pazCol ) *pazCol = (const char **)azCol; 611e8d5648eSdan if( pabPK ) *pabPK = abPK; 612e8d5648eSdan }else{ 613e8d5648eSdan if( pazCol ) *pazCol = 0; 614e8d5648eSdan if( pabPK ) *pabPK = 0; 615e8d5648eSdan if( pzTab ) *pzTab = 0; 616e8d5648eSdan sqlite3_free(pFree); 617e8d5648eSdan } 618e8d5648eSdan sqlite3_finalize(pStmt); 619e8d5648eSdan return rc; 620e8d5648eSdan } 621e8d5648eSdan 622e8d5648eSdan /* 623296c7658Sdan ** This function is only called from within a pre-update handler for a 624296c7658Sdan ** write to table pTab, part of session pSession. If this is the first 625296c7658Sdan ** write to this table, set the SessionTable.nCol variable to the number 626296c7658Sdan ** of columns in the table. 627296c7658Sdan ** 628296c7658Sdan ** Otherwise, if this is not the first time this table has been written 629296c7658Sdan ** to, check that the number of columns in the table has not changed. If 630296c7658Sdan ** it has not, return zero. 631296c7658Sdan ** 632296c7658Sdan ** If the number of columns in the table has changed since the last write 633296c7658Sdan ** was recorded, set the session error-code to SQLITE_SCHEMA and return 634296c7658Sdan ** non-zero. Users are not allowed to change the number of columns in a table 635296c7658Sdan ** for which changes are being recorded by the session module. If they do so, 636296c7658Sdan ** it is an error. 637296c7658Sdan */ 6384fccf43aSdan static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ 6394fccf43aSdan if( pTab->nCol==0 ){ 640e8d5648eSdan assert( pTab->azCol==0 || pTab->abPK==0 ); 6414fccf43aSdan pTab->nCol = sqlite3_preupdate_count(pSession->db); 642e8d5648eSdan pSession->rc = sessionTableInfo(pSession->db, pSession->zDb, 643e8d5648eSdan pTab->zName, pTab->nCol, 0, &pTab->azCol, &pTab->abPK 644e8d5648eSdan ); 645296c7658Sdan }else if( pTab->nCol!=sqlite3_preupdate_count(pSession->db) ){ 6464fccf43aSdan pSession->rc = SQLITE_SCHEMA; 6474fccf43aSdan } 648e8d5648eSdan return pSession->rc; 649e8d5648eSdan } 650e8d5648eSdan 651e8d5648eSdan static void sessionPreupdateOneChange( 652e8d5648eSdan int op, 653e8d5648eSdan sqlite3_session *pSession, 654e8d5648eSdan SessionTable *pTab 655e8d5648eSdan ){ 656e8d5648eSdan sqlite3 *db = pSession->db; 657e8d5648eSdan SessionChange *pChange; 658e8d5648eSdan SessionChange *pC; 659e8d5648eSdan int iHash; 660e8d5648eSdan int rc = SQLITE_OK; 661e8d5648eSdan 662e8d5648eSdan if( pSession->rc ) return; 663e8d5648eSdan 664e8d5648eSdan /* Load table details if required */ 665e8d5648eSdan if( sessionInitTable(pSession, pTab) ) return; 666e8d5648eSdan 667e8d5648eSdan /* Grow the hash table if required */ 668e8d5648eSdan if( sessionGrowHash(pSession, pTab) ) return; 669e8d5648eSdan 670e8d5648eSdan /* Search the hash table for an existing entry for rowid=iKey2. If 671e8d5648eSdan ** one is found, store a pointer to it in pChange and unlink it from 672e8d5648eSdan ** the hash table. Otherwise, set pChange to NULL. 673e8d5648eSdan */ 674e8d5648eSdan rc = sessionPreupdateHash(db, pTab, op==SQLITE_INSERT, &iHash); 675e8d5648eSdan for(pC=pTab->apChange[iHash]; rc==SQLITE_OK && pC; pC=pC->pNext){ 676e8d5648eSdan int bEqual; 677e8d5648eSdan rc = sessionPreupdateEqual(db, pTab, pC, op==SQLITE_INSERT, &bEqual); 678e8d5648eSdan if( bEqual ) break; 679e8d5648eSdan } 680e8d5648eSdan if( pC==0 ){ 681e8d5648eSdan /* Create a new change object containing all the old values (if 682e8d5648eSdan ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK 683e8d5648eSdan ** values (if this is an INSERT). */ 684e8d5648eSdan int nByte; /* Number of bytes to allocate */ 685e8d5648eSdan int i; /* Used to iterate through columns */ 686e8d5648eSdan 687e8d5648eSdan pTab->nEntry++; 688e8d5648eSdan 689e8d5648eSdan /* Figure out how large an allocation is required */ 690e8d5648eSdan nByte = sizeof(SessionChange); 691e8d5648eSdan for(i=0; i<pTab->nCol && rc==SQLITE_OK; i++){ 692e8d5648eSdan sqlite3_value *p = 0; 693e8d5648eSdan if( op!=SQLITE_INSERT ){ 694e8d5648eSdan rc = sqlite3_preupdate_old(pSession->db, i, &p); 695e8d5648eSdan }else if( 1 || pTab->abPK[i] ){ 696e8d5648eSdan rc = sqlite3_preupdate_new(pSession->db, i, &p); 697e8d5648eSdan } 698e8d5648eSdan if( p && rc==SQLITE_OK ){ 699e8d5648eSdan rc = sessionSerializeValue(0, p, &nByte); 700e8d5648eSdan } 701e8d5648eSdan } 702e8d5648eSdan 703e8d5648eSdan /* Allocate the change object */ 704e8d5648eSdan pChange = (SessionChange *)sqlite3_malloc(nByte); 705e8d5648eSdan if( !pChange ){ 706e8d5648eSdan rc = SQLITE_NOMEM; 707e8d5648eSdan }else{ 708e8d5648eSdan memset(pChange, 0, sizeof(SessionChange)); 709e8d5648eSdan pChange->aRecord = (u8 *)&pChange[1]; 710e8d5648eSdan } 711e8d5648eSdan 712e8d5648eSdan /* Populate the change object */ 713e8d5648eSdan nByte = 0; 714e8d5648eSdan for(i=0; i<pTab->nCol && rc==SQLITE_OK; i++){ 715e8d5648eSdan sqlite3_value *p = 0; 716e8d5648eSdan if( op!=SQLITE_INSERT ){ 717e8d5648eSdan rc = sqlite3_preupdate_old(pSession->db, i, &p); 718e8d5648eSdan }else if( 1 || pTab->abPK[i] ){ 719e8d5648eSdan rc = sqlite3_preupdate_new(pSession->db, i, &p); 720e8d5648eSdan } 721e8d5648eSdan if( p && rc==SQLITE_OK ){ 722e8d5648eSdan rc = sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte); 723e8d5648eSdan } 724e8d5648eSdan } 725e8d5648eSdan pChange->nRecord = nByte; 726e8d5648eSdan 727e8d5648eSdan /* If an error has occurred, mark the session object as failed. */ 728e8d5648eSdan if( rc!=SQLITE_OK ){ 729e8d5648eSdan sqlite3_free(pChange); 730e8d5648eSdan pSession->rc = rc; 731e8d5648eSdan }else{ 732e8d5648eSdan /* Add the change back to the hash-table */ 733e8d5648eSdan pChange->bInsert = (op==SQLITE_INSERT); 734e8d5648eSdan pChange->pNext = pTab->apChange[iHash]; 735e8d5648eSdan pTab->apChange[iHash] = pChange; 736e8d5648eSdan } 737e8d5648eSdan } 7384fccf43aSdan } 7394fccf43aSdan 7404fccf43aSdan /* 7414fccf43aSdan ** The 'pre-update' hook registered by this module with SQLite databases. 7424fccf43aSdan */ 7434fccf43aSdan static void xPreUpdate( 7444fccf43aSdan void *pCtx, /* Copy of third arg to preupdate_hook() */ 7454fccf43aSdan sqlite3 *db, /* Database handle */ 7464fccf43aSdan int op, /* SQLITE_UPDATE, DELETE or INSERT */ 7474fccf43aSdan char const *zDb, /* Database name */ 7484fccf43aSdan char const *zName, /* Table name */ 7494fccf43aSdan sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */ 7504fccf43aSdan sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */ 7514fccf43aSdan ){ 7524fccf43aSdan sqlite3_session *pSession; 7534fccf43aSdan int nDb = strlen(zDb); 7544fccf43aSdan int nName = strlen(zDb); 7554fccf43aSdan 7564c220252Sdan assert( sqlite3_mutex_held(db->mutex) ); 7574c220252Sdan 7584fccf43aSdan for(pSession=(sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){ 7594fccf43aSdan SessionTable *pTab; 760296c7658Sdan 761e8d5648eSdan /* If this session is attached to a different database ("main", "temp" 762e8d5648eSdan ** etc.), or if it is not currently enabled, there is nothing to do. Skip 763e8d5648eSdan ** to the next session object attached to this database. */ 764296c7658Sdan if( pSession->bEnable==0 ) continue; 7654fccf43aSdan if( pSession->rc ) continue; 7664fccf43aSdan if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue; 767296c7658Sdan 7684fccf43aSdan for(pTab=pSession->pTable; pTab; pTab=pTab->pNext){ 7694fccf43aSdan if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ){ 770e8d5648eSdan sessionPreupdateOneChange(op, pSession, pTab); 771e8d5648eSdan if( op==SQLITE_UPDATE ){ 772e8d5648eSdan sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab); 7734fccf43aSdan } 7744fccf43aSdan break; 7754fccf43aSdan } 7764fccf43aSdan } 7774fccf43aSdan } 778296c7658Sdan } 7794fccf43aSdan 7804fccf43aSdan /* 7814fccf43aSdan ** Create a session object. This session object will record changes to 7824fccf43aSdan ** database zDb attached to connection db. 7834fccf43aSdan */ 7844fccf43aSdan int sqlite3session_create( 7854fccf43aSdan sqlite3 *db, /* Database handle */ 7864fccf43aSdan const char *zDb, /* Name of db (e.g. "main") */ 7874fccf43aSdan sqlite3_session **ppSession /* OUT: New session object */ 7884fccf43aSdan ){ 789296c7658Sdan sqlite3_session *pNew; /* Newly allocated session object */ 790296c7658Sdan sqlite3_session *pOld; /* Session object already attached to db */ 7914fccf43aSdan int nDb = strlen(zDb); /* Length of zDb in bytes */ 7924fccf43aSdan 793296c7658Sdan /* Zero the output value in case an error occurs. */ 7944fccf43aSdan *ppSession = 0; 7954fccf43aSdan 7964fccf43aSdan /* Allocate and populate the new session object. */ 7974fccf43aSdan pNew = (sqlite3_session *)sqlite3_malloc(sizeof(sqlite3_session) + nDb + 1); 7984fccf43aSdan if( !pNew ) return SQLITE_NOMEM; 7994fccf43aSdan memset(pNew, 0, sizeof(sqlite3_session)); 8004fccf43aSdan pNew->db = db; 8014fccf43aSdan pNew->zDb = (char *)&pNew[1]; 802296c7658Sdan pNew->bEnable = 1; 8034fccf43aSdan memcpy(pNew->zDb, zDb, nDb+1); 8044fccf43aSdan 8054fccf43aSdan /* Add the new session object to the linked list of session objects 8064fccf43aSdan ** attached to database handle $db. Do this under the cover of the db 8074fccf43aSdan ** handle mutex. */ 8084fccf43aSdan sqlite3_mutex_enter(sqlite3_db_mutex(db)); 8094fccf43aSdan pOld = (sqlite3_session*)sqlite3_preupdate_hook(db, xPreUpdate, (void*)pNew); 8104fccf43aSdan pNew->pNext = pOld; 8114fccf43aSdan sqlite3_mutex_leave(sqlite3_db_mutex(db)); 8124fccf43aSdan 8134fccf43aSdan *ppSession = pNew; 8144fccf43aSdan return SQLITE_OK; 8154fccf43aSdan } 8164fccf43aSdan 8174fccf43aSdan /* 8184fccf43aSdan ** Delete a session object previously allocated using sqlite3session_create(). 8194fccf43aSdan */ 8204fccf43aSdan void sqlite3session_delete(sqlite3_session *pSession){ 8214fccf43aSdan sqlite3 *db = pSession->db; 8224fccf43aSdan sqlite3_session *pHead; 8234fccf43aSdan sqlite3_session **pp; 8244fccf43aSdan 825296c7658Sdan /* Unlink the session from the linked list of sessions attached to the 826296c7658Sdan ** database handle. Hold the db mutex while doing so. */ 8274fccf43aSdan sqlite3_mutex_enter(sqlite3_db_mutex(db)); 8284fccf43aSdan pHead = (sqlite3_session*)sqlite3_preupdate_hook(db, 0, 0); 8294fccf43aSdan for(pp=&pHead; (*pp)!=pSession; pp=&((*pp)->pNext)); 8304fccf43aSdan *pp = (*pp)->pNext; 8314fccf43aSdan if( pHead ) sqlite3_preupdate_hook(db, xPreUpdate, (void *)pHead); 8324fccf43aSdan sqlite3_mutex_leave(sqlite3_db_mutex(db)); 8334fccf43aSdan 834296c7658Sdan /* Delete all attached table objects. And the contents of their 835296c7658Sdan ** associated hash-tables. */ 8364fccf43aSdan while( pSession->pTable ){ 8374fccf43aSdan int i; 8384fccf43aSdan SessionTable *pTab = pSession->pTable; 8394fccf43aSdan pSession->pTable = pTab->pNext; 8404fccf43aSdan for(i=0; i<pTab->nChange; i++){ 8414fccf43aSdan SessionChange *p; 8424fccf43aSdan SessionChange *pNext; 8434fccf43aSdan for(p=pTab->apChange[i]; p; p=pNext){ 8444fccf43aSdan pNext = p->pNext; 8454fccf43aSdan sqlite3_free(p); 8464fccf43aSdan } 8474fccf43aSdan } 848e8d5648eSdan sqlite3_free(pTab->azCol); 8494fccf43aSdan sqlite3_free(pTab->apChange); 8504fccf43aSdan sqlite3_free(pTab); 8514fccf43aSdan } 8524fccf43aSdan 853296c7658Sdan /* Free the session object itself. */ 8544fccf43aSdan sqlite3_free(pSession); 8554fccf43aSdan } 8564fccf43aSdan 8574fccf43aSdan /* 8584fccf43aSdan ** Attach a table to a session. All subsequent changes made to the table 8594fccf43aSdan ** while the session object is enabled will be recorded. 8604fccf43aSdan ** 8614fccf43aSdan ** Only tables that have a PRIMARY KEY defined may be attached. It does 8624fccf43aSdan ** not matter if the PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias) 8634fccf43aSdan ** or not. 8644fccf43aSdan */ 8654fccf43aSdan int sqlite3session_attach( 8664fccf43aSdan sqlite3_session *pSession, /* Session object */ 8674fccf43aSdan const char *zName /* Table name */ 8684fccf43aSdan ){ 869296c7658Sdan SessionTable *pTab; /* New table object (if required) */ 870296c7658Sdan int nName; /* Number of bytes in string zName */ 8714c220252Sdan int rc = SQLITE_OK; 8724c220252Sdan 8734c220252Sdan sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db)); 8744fccf43aSdan 8754fccf43aSdan /* First search for an existing entry. If one is found, this call is 8764fccf43aSdan ** a no-op. Return early. */ 8774fccf43aSdan nName = strlen(zName); 8784fccf43aSdan for(pTab=pSession->pTable; pTab; pTab=pTab->pNext){ 8794c220252Sdan if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ) break; 8804fccf43aSdan } 8814fccf43aSdan 8824c220252Sdan if( !pTab ){ 8834fccf43aSdan /* Allocate new SessionTable object. */ 8844fccf43aSdan pTab = (SessionTable *)sqlite3_malloc(sizeof(SessionTable) + nName + 1); 8854c220252Sdan if( !pTab ){ 8864c220252Sdan rc = SQLITE_NOMEM; 8874c220252Sdan }else{ 8884fccf43aSdan /* Populate the new SessionTable object and link it into the list. */ 8894fccf43aSdan memset(pTab, 0, sizeof(SessionTable)); 8904fccf43aSdan pTab->zName = (char *)&pTab[1]; 8914fccf43aSdan memcpy(pTab->zName, zName, nName+1); 8924fccf43aSdan pTab->pNext = pSession->pTable; 8934fccf43aSdan pSession->pTable = pTab; 8944c220252Sdan } 8954c220252Sdan } 8964fccf43aSdan 8974c220252Sdan sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db)); 8984c220252Sdan return rc; 8994fccf43aSdan } 9004fccf43aSdan 901296c7658Sdan /* 902296c7658Sdan ** Ensure that there is room in the buffer to append nByte bytes of data. 903296c7658Sdan ** If not, use sqlite3_realloc() to grow the buffer so that there is. 904296c7658Sdan ** 905296c7658Sdan ** If successful, return zero. Otherwise, if an OOM condition is encountered, 906296c7658Sdan ** set *pRc to SQLITE_NOMEM and return non-zero. 907296c7658Sdan */ 9084fccf43aSdan static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){ 9094fccf43aSdan if( p->nAlloc-p->nBuf<nByte ){ 9104fccf43aSdan u8 *aNew; 9114fccf43aSdan int nNew = p->nAlloc ? p->nAlloc : 128; 9124fccf43aSdan do { 9134fccf43aSdan nNew = nNew*2; 9144fccf43aSdan }while( nNew<(p->nAlloc+nByte) ); 9154fccf43aSdan 9164fccf43aSdan aNew = (u8 *)sqlite3_realloc(p->aBuf, nNew); 9174fccf43aSdan if( 0==aNew ){ 9184fccf43aSdan *pRc = SQLITE_NOMEM; 9194fccf43aSdan return 1; 9204fccf43aSdan } 9214fccf43aSdan p->aBuf = aNew; 9224fccf43aSdan p->nAlloc = nNew; 9234fccf43aSdan } 9244fccf43aSdan return 0; 9254fccf43aSdan } 9264fccf43aSdan 927296c7658Sdan /* 928296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is 929296c7658Sdan ** called. Otherwise, append a single byte to the buffer. 930296c7658Sdan ** 931296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before 932296c7658Sdan ** returning. 933296c7658Sdan */ 9344fccf43aSdan static void sessionAppendByte(SessionBuffer *p, u8 v, int *pRc){ 9354fccf43aSdan if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, 1, pRc) ){ 9364fccf43aSdan p->aBuf[p->nBuf++] = v; 9374fccf43aSdan } 9384fccf43aSdan } 9394fccf43aSdan 940296c7658Sdan /* 941296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is 942296c7658Sdan ** called. Otherwise, append a single varint to the buffer. 943296c7658Sdan ** 944296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before 945296c7658Sdan ** returning. 946296c7658Sdan */ 9474fccf43aSdan static void sessionAppendVarint(SessionBuffer *p, sqlite3_int64 v, int *pRc){ 9484fccf43aSdan if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, 9, pRc) ){ 9494fccf43aSdan p->nBuf += sessionVarintPut(&p->aBuf[p->nBuf], v); 9504fccf43aSdan } 9514fccf43aSdan } 9524fccf43aSdan 953296c7658Sdan /* 954296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is 955296c7658Sdan ** called. Otherwise, append a blob of data to the buffer. 956296c7658Sdan ** 957296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before 958296c7658Sdan ** returning. 959296c7658Sdan */ 9604fccf43aSdan static void sessionAppendBlob( 9614fccf43aSdan SessionBuffer *p, 9624fccf43aSdan const u8 *aBlob, 9634fccf43aSdan int nBlob, 9644fccf43aSdan int *pRc 9654fccf43aSdan ){ 9664fccf43aSdan if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nBlob, pRc) ){ 9674fccf43aSdan memcpy(&p->aBuf[p->nBuf], aBlob, nBlob); 9684fccf43aSdan p->nBuf += nBlob; 9694fccf43aSdan } 9704fccf43aSdan } 9714fccf43aSdan 972296c7658Sdan /* 973296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is 974296c7658Sdan ** called. Otherwise, append a string to the buffer. All bytes in the string 975296c7658Sdan ** up to (but not including) the nul-terminator are written to the buffer. 976296c7658Sdan ** 977296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before 978296c7658Sdan ** returning. 979296c7658Sdan */ 980d5f0767cSdan static void sessionAppendStr( 981d5f0767cSdan SessionBuffer *p, 982d5f0767cSdan const char *zStr, 983d5f0767cSdan int *pRc 984d5f0767cSdan ){ 985d5f0767cSdan int nStr = strlen(zStr); 986d5f0767cSdan if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nStr, pRc) ){ 987d5f0767cSdan memcpy(&p->aBuf[p->nBuf], zStr, nStr); 988d5f0767cSdan p->nBuf += nStr; 989d5f0767cSdan } 990d5f0767cSdan } 991d5f0767cSdan 992296c7658Sdan /* 993296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is 994296c7658Sdan ** called. Otherwise, append the string representation of integer iVal 995296c7658Sdan ** to the buffer. No nul-terminator is written. 996296c7658Sdan ** 997296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before 998296c7658Sdan ** returning. 999296c7658Sdan */ 1000d5f0767cSdan static void sessionAppendInteger( 1001296c7658Sdan SessionBuffer *p, /* Buffer to append to */ 1002296c7658Sdan int iVal, /* Value to write the string rep. of */ 1003296c7658Sdan int *pRc /* IN/OUT: Error code */ 1004d5f0767cSdan ){ 1005d5f0767cSdan char aBuf[24]; 1006d5f0767cSdan sqlite3_snprintf(sizeof(aBuf)-1, aBuf, "%d", iVal); 1007d5f0767cSdan sessionAppendStr(p, aBuf, pRc); 1008d5f0767cSdan } 1009d5f0767cSdan 1010296c7658Sdan /* 1011296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is 1012296c7658Sdan ** called. Otherwise, append the string zStr enclosed in quotes (") and 1013296c7658Sdan ** with any embedded quote characters escaped to the buffer. No 1014296c7658Sdan ** nul-terminator byte is written. 1015296c7658Sdan ** 1016296c7658Sdan ** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before 1017296c7658Sdan ** returning. 1018296c7658Sdan */ 1019d5f0767cSdan static void sessionAppendIdent( 1020296c7658Sdan SessionBuffer *p, /* Buffer to a append to */ 1021296c7658Sdan const char *zStr, /* String to quote, escape and append */ 1022296c7658Sdan int *pRc /* IN/OUT: Error code */ 1023d5f0767cSdan ){ 1024d5f0767cSdan int nStr = strlen(zStr)*2 + 2 + 1; 1025d5f0767cSdan if( *pRc==SQLITE_OK && 0==sessionBufferGrow(p, nStr, pRc) ){ 1026d5f0767cSdan char *zOut = (char *)&p->aBuf[p->nBuf]; 1027d5f0767cSdan const char *zIn = zStr; 1028d5f0767cSdan *zOut++ = '"'; 1029d5f0767cSdan while( *zIn ){ 1030d5f0767cSdan if( *zIn=='"' ) *zOut++ = '"'; 1031d5f0767cSdan *zOut++ = *(zIn++); 1032d5f0767cSdan } 1033d5f0767cSdan *zOut++ = '"'; 1034d5f0767cSdan p->nBuf = ((u8 *)zOut - p->aBuf); 1035d5f0767cSdan } 1036d5f0767cSdan } 1037d5f0767cSdan 1038296c7658Sdan /* 1039296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is 1040296c7658Sdan ** called. Otherwse, it appends the serialized version of the value stored 1041296c7658Sdan ** in column iCol of the row that SQL statement pStmt currently points 1042296c7658Sdan ** to to the buffer. 1043296c7658Sdan */ 10444fccf43aSdan static void sessionAppendCol( 1045296c7658Sdan SessionBuffer *p, /* Buffer to append to */ 1046296c7658Sdan sqlite3_stmt *pStmt, /* Handle pointing to row containing value */ 1047296c7658Sdan int iCol, /* Column to read value from */ 1048296c7658Sdan int *pRc /* IN/OUT: Error code */ 10494fccf43aSdan ){ 10504fccf43aSdan if( *pRc==SQLITE_OK ){ 10514fccf43aSdan int eType = sqlite3_column_type(pStmt, iCol); 10524fccf43aSdan sessionAppendByte(p, (u8)eType, pRc); 10534fccf43aSdan if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ 10544fccf43aSdan sqlite3_int64 i; 10554fccf43aSdan u8 aBuf[8]; 10564fccf43aSdan if( eType==SQLITE_INTEGER ){ 10574fccf43aSdan i = sqlite3_column_int64(pStmt, iCol); 10584fccf43aSdan }else{ 10594fccf43aSdan double r = sqlite3_column_double(pStmt, iCol); 10604fccf43aSdan memcpy(&i, &r, 8); 10614fccf43aSdan } 1062296c7658Sdan sessionPutI64(aBuf, i); 10634fccf43aSdan sessionAppendBlob(p, aBuf, 8, pRc); 10644fccf43aSdan } 10654fccf43aSdan if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){ 10664fccf43aSdan int nByte = sqlite3_column_bytes(pStmt, iCol); 10674fccf43aSdan sessionAppendVarint(p, nByte, pRc); 10684fccf43aSdan sessionAppendBlob(p, eType==SQLITE_BLOB ? 10694fccf43aSdan sqlite3_column_blob(pStmt, iCol) : sqlite3_column_text(pStmt, iCol), 10704fccf43aSdan nByte, pRc 10714fccf43aSdan ); 10724fccf43aSdan } 10734fccf43aSdan } 10744fccf43aSdan } 10754fccf43aSdan 1076296c7658Sdan /* 1077296c7658Sdan ** This function is a no-op if *pRc is other than SQLITE_OK when it is 1078296c7658Sdan ** called. 1079296c7658Sdan ** 1080296c7658Sdan ** Otherwse, if *pRc is SQLITE_OK, then it appends an update change to 1081296c7658Sdan ** the buffer (see the comments under "CHANGESET FORMAT" at the top of the 1082296c7658Sdan ** file). An update change consists of: 1083296c7658Sdan ** 1084296c7658Sdan ** 1 byte: SQLITE_UPDATE (0x17) 1085296c7658Sdan ** n bytes: old.* record (see RECORD FORMAT) 1086296c7658Sdan ** m bytes: new.* record (see RECORD FORMAT) 1087296c7658Sdan ** 1088296c7658Sdan ** The SessionChange object passed as the third argument contains the 1089296c7658Sdan ** values that were stored in the row when the session began (the old.* 1090296c7658Sdan ** values). The statement handle passed as the second argument points 1091296c7658Sdan ** at the current version of the row (the new.* values). 1092296c7658Sdan ** 1093296c7658Sdan ** If all of the old.* values are equal to their corresponding new.* value 1094296c7658Sdan ** (i.e. nothing has changed), then no data at all is appended to the buffer. 1095296c7658Sdan ** 1096296c7658Sdan ** Otherwise, the old.* record contains all primary key values and the 1097296c7658Sdan ** original values of any fields that have been modified. The new.* record 1098296c7658Sdan ** contains the new values of only those fields that have been modified. 1099296c7658Sdan */ 11004fccf43aSdan static void sessionAppendUpdate( 1101296c7658Sdan SessionBuffer *pBuf, /* Buffer to append to */ 1102296c7658Sdan sqlite3_stmt *pStmt, /* Statement handle pointing at new row */ 1103296c7658Sdan SessionChange *p, /* Object containing old values */ 1104296c7658Sdan u8 *abPK, /* Boolean array - true for PK columns */ 1105296c7658Sdan int *pRc /* IN/OUT: Error code */ 11064fccf43aSdan ){ 11074fccf43aSdan if( *pRc==SQLITE_OK ){ 1108296c7658Sdan SessionBuffer buf2 = {0,0,0}; /* Buffer to accumulate new.* record in */ 1109296c7658Sdan int bNoop = 1; /* Set to zero if any values are modified */ 1110296c7658Sdan int i; /* Used to iterate through columns */ 1111296c7658Sdan u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */ 1112296c7658Sdan 11134fccf43aSdan sessionAppendByte(pBuf, SQLITE_UPDATE, pRc); 11144fccf43aSdan for(i=0; i<sqlite3_column_count(pStmt); i++){ 111537f133ecSdan int bChanged = 0; 11164fccf43aSdan int nAdvance; 11174fccf43aSdan int eType = *pCsr; 11184fccf43aSdan switch( eType ){ 11194fccf43aSdan case SQLITE_NULL: 11204fccf43aSdan nAdvance = 1; 11214fccf43aSdan if( sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){ 112237f133ecSdan bChanged = 1; 11234fccf43aSdan } 11244fccf43aSdan break; 11254fccf43aSdan 11264fccf43aSdan case SQLITE_FLOAT: 11274fccf43aSdan case SQLITE_INTEGER: { 11284fccf43aSdan nAdvance = 9; 11294fccf43aSdan if( eType==sqlite3_column_type(pStmt, i) ){ 11304fccf43aSdan sqlite3_int64 iVal = sessionGetI64(&pCsr[1]); 11314fccf43aSdan if( eType==SQLITE_INTEGER ){ 11324fccf43aSdan if( iVal==sqlite3_column_int64(pStmt, i) ) break; 11334fccf43aSdan }else{ 11344fccf43aSdan double dVal; 11354fccf43aSdan memcpy(&dVal, &iVal, 8); 11364fccf43aSdan if( dVal==sqlite3_column_double(pStmt, i) ) break; 11374fccf43aSdan } 11384fccf43aSdan } 113937f133ecSdan bChanged = 1; 11404fccf43aSdan break; 11414fccf43aSdan } 11424fccf43aSdan 11434fccf43aSdan case SQLITE_TEXT: 11444fccf43aSdan case SQLITE_BLOB: { 11454fccf43aSdan int nByte; 11464fccf43aSdan int nHdr = 1 + sessionVarintGet(&pCsr[1], &nByte); 11474fccf43aSdan nAdvance = nHdr + nByte; 11484fccf43aSdan if( eType==sqlite3_column_type(pStmt, i) 11494fccf43aSdan && nByte==sqlite3_column_bytes(pStmt, i) 11504fccf43aSdan && 0==memcmp(&pCsr[nHdr], sqlite3_column_blob(pStmt, i), nByte) 11514fccf43aSdan ){ 11524fccf43aSdan break; 11534fccf43aSdan } 115437f133ecSdan bChanged = 1; 11554fccf43aSdan } 11564fccf43aSdan } 11574fccf43aSdan 115837f133ecSdan if( bChanged || abPK[i] ){ 115937f133ecSdan sessionAppendBlob(pBuf, pCsr, nAdvance, pRc); 11604fccf43aSdan }else{ 116137f133ecSdan sessionAppendByte(pBuf, 0, pRc); 116237f133ecSdan } 116337f133ecSdan 116437f133ecSdan if( bChanged ){ 11654fccf43aSdan sessionAppendCol(&buf2, pStmt, i, pRc); 11664fccf43aSdan bNoop = 0; 116737f133ecSdan }else{ 116837f133ecSdan sessionAppendByte(&buf2, 0, pRc); 11694fccf43aSdan } 117037f133ecSdan 11714fccf43aSdan pCsr += nAdvance; 11724fccf43aSdan } 11734fccf43aSdan 11744fccf43aSdan if( bNoop ){ 11754fccf43aSdan pBuf->nBuf -= (1 + sqlite3_column_count(pStmt)); 11764fccf43aSdan }else{ 11774fccf43aSdan sessionAppendBlob(pBuf, buf2.aBuf, buf2.nBuf, pRc); 11784fccf43aSdan sqlite3_free(buf2.aBuf); 11794fccf43aSdan } 11804fccf43aSdan } 1181d5f0767cSdan } 11824fccf43aSdan 1183e8d5648eSdan static int sessionSelectStmt( 1184e8d5648eSdan sqlite3 *db, /* Database handle */ 1185e8d5648eSdan const char *zTab, /* Table name */ 1186e8d5648eSdan int nCol, 1187e8d5648eSdan const char **azCol, 1188e8d5648eSdan u8 *abPK, 1189e8d5648eSdan sqlite3_stmt **ppStmt 1190d5f0767cSdan ){ 1191e8d5648eSdan int rc = SQLITE_OK; 1192d5f0767cSdan int i; 1193e8d5648eSdan const char *zSep = ""; 1194e8d5648eSdan SessionBuffer buf = {0, 0, 0}; 1195d5f0767cSdan 1196e8d5648eSdan sessionAppendStr(&buf, "SELECT * FROM ", &rc); 1197e8d5648eSdan sessionAppendIdent(&buf, zTab, &rc); 1198e8d5648eSdan sessionAppendStr(&buf, " WHERE ", &rc); 1199e8d5648eSdan for(i=0; i<nCol; i++){ 1200e8d5648eSdan if( abPK[i] ){ 1201e8d5648eSdan sessionAppendStr(&buf, zSep, &rc); 1202e8d5648eSdan sessionAppendIdent(&buf, azCol[i], &rc); 1203e8d5648eSdan sessionAppendStr(&buf, " = ?", &rc); 1204e8d5648eSdan sessionAppendInteger(&buf, i+1, &rc); 1205e8d5648eSdan zSep = " AND "; 1206d5f0767cSdan } 1207d5f0767cSdan } 1208d5f0767cSdan if( rc==SQLITE_OK ){ 1209e8d5648eSdan rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, ppStmt, 0); 1210d5f0767cSdan } 1211e8d5648eSdan sqlite3_free(buf.aBuf); 1212e8d5648eSdan return rc; 1213d5f0767cSdan } 1214d5f0767cSdan 1215e8d5648eSdan static int sessionSelectBind( 1216e8d5648eSdan sqlite3_stmt *pSelect, 1217e8d5648eSdan int nCol, 1218e8d5648eSdan u8 *abPK, 1219e8d5648eSdan u8 *aRecord, 1220e8d5648eSdan int nRecord 1221e8d5648eSdan ){ 1222e8d5648eSdan int i; 1223e8d5648eSdan int rc = SQLITE_OK; 1224e8d5648eSdan u8 *a = aRecord; 1225d5f0767cSdan 1226e8d5648eSdan for(i=0; i<nCol && rc==SQLITE_OK; i++){ 1227e8d5648eSdan int eType = *a++; 1228e8d5648eSdan 1229e8d5648eSdan switch( eType ){ 1230e8d5648eSdan case SQLITE_NULL: 1231e8d5648eSdan if( abPK[i] ) rc = sqlite3_bind_null(pSelect, i+1); 1232e8d5648eSdan break; 1233e8d5648eSdan 1234e8d5648eSdan case SQLITE_INTEGER: { 1235e8d5648eSdan if( abPK[i] ){ 1236e8d5648eSdan i64 iVal = sessionGetI64(a); 1237e8d5648eSdan rc = sqlite3_bind_int64(pSelect, i+1, iVal); 1238e8d5648eSdan } 1239e8d5648eSdan a += 8; 1240e8d5648eSdan break; 1241d5f0767cSdan } 1242296c7658Sdan 1243e8d5648eSdan case SQLITE_FLOAT: { 1244e8d5648eSdan if( abPK[i] ){ 1245e8d5648eSdan double rVal; 1246e8d5648eSdan i64 iVal = sessionGetI64(a); 1247e8d5648eSdan memcpy(&rVal, &iVal, 8); 1248e8d5648eSdan rc = sqlite3_bind_int64(pSelect, i+1, rVal); 1249d5f0767cSdan } 1250e8d5648eSdan a += 8; 1251e8d5648eSdan break; 1252e8d5648eSdan } 1253e8d5648eSdan 1254e8d5648eSdan case SQLITE_TEXT: { 1255e8d5648eSdan int n; 1256e8d5648eSdan a += sessionVarintGet(a, &n); 1257e8d5648eSdan if( abPK[i] ){ 1258e8d5648eSdan rc = sqlite3_bind_text(pSelect, i+1, (char *)a, n, SQLITE_TRANSIENT); 1259e8d5648eSdan } 1260e8d5648eSdan a += n; 1261e8d5648eSdan break; 1262e8d5648eSdan } 1263e8d5648eSdan 1264e8d5648eSdan case SQLITE_BLOB: { 1265e8d5648eSdan int n; 1266e8d5648eSdan a += sessionVarintGet(a, &n); 1267e8d5648eSdan if( abPK[i] ){ 1268e8d5648eSdan rc = sqlite3_bind_blob(pSelect, i+1, a, n, SQLITE_TRANSIENT); 1269e8d5648eSdan } 1270e8d5648eSdan a += n; 1271e8d5648eSdan break; 1272e8d5648eSdan } 1273e8d5648eSdan } 1274e8d5648eSdan } 1275e8d5648eSdan 1276d5f0767cSdan return rc; 12774fccf43aSdan } 12784fccf43aSdan 12794fccf43aSdan /* 12804fccf43aSdan ** Obtain a changeset object containing all changes recorded by the 12814fccf43aSdan ** session object passed as the first argument. 12824fccf43aSdan ** 12834fccf43aSdan ** It is the responsibility of the caller to eventually free the buffer 12844fccf43aSdan ** using sqlite3_free(). 12854fccf43aSdan */ 12864fccf43aSdan int sqlite3session_changeset( 12874fccf43aSdan sqlite3_session *pSession, /* Session object */ 12884fccf43aSdan int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ 12894fccf43aSdan void **ppChangeset /* OUT: Buffer containing changeset */ 12904fccf43aSdan ){ 1291296c7658Sdan sqlite3 *db = pSession->db; /* Source database handle */ 1292296c7658Sdan SessionTable *pTab; /* Used to iterate through attached tables */ 1293296c7658Sdan SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */ 1294296c7658Sdan int rc; /* Return code */ 12954fccf43aSdan 12964c220252Sdan sqlite3_mutex_enter(sqlite3_db_mutex(db)); 12974c220252Sdan 1298296c7658Sdan /* Zero the output variables in case an error occurs. If this session 1299296c7658Sdan ** object is already in the error state (sqlite3_session.rc != SQLITE_OK), 1300296c7658Sdan ** this call will be a no-op. */ 13014fccf43aSdan *pnChangeset = 0; 13024fccf43aSdan *ppChangeset = 0; 13034fccf43aSdan rc = pSession->rc; 13044fccf43aSdan 13054fccf43aSdan for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){ 13064fccf43aSdan if( pTab->nEntry ){ 1307e8d5648eSdan int nCol = pTab->nCol; /* Local copy of member variable */ 1308e8d5648eSdan u8 *abPK = pTab->abPK; /* Local copy of member variable */ 13094fccf43aSdan int i; 13104fccf43aSdan sqlite3_stmt *pStmt = 0; 13114fccf43aSdan int bNoop = 1; 13124fccf43aSdan int nRewind = buf.nBuf; 13134fccf43aSdan 13144fccf43aSdan /* Write a table header */ 13154fccf43aSdan sessionAppendByte(&buf, 'T', &rc); 1316e8d5648eSdan sessionAppendVarint(&buf, nCol, &rc); 13174fccf43aSdan sessionAppendBlob(&buf, (u8 *)pTab->zName, strlen(pTab->zName)+1, &rc); 13184fccf43aSdan 13194fccf43aSdan /* Build and compile a statement to execute: */ 13204fccf43aSdan if( rc==SQLITE_OK ){ 1321e8d5648eSdan rc = sessionSelectStmt(db, pTab->zName, nCol, pTab->azCol, abPK,&pStmt); 13224fccf43aSdan } 13234fccf43aSdan 1324e8d5648eSdan if( rc==SQLITE_OK && nCol!=sqlite3_column_count(pStmt) ){ 13254fccf43aSdan rc = SQLITE_SCHEMA; 13264fccf43aSdan } 13274fccf43aSdan 13284fccf43aSdan for(i=0; i<pTab->nChange; i++){ 1329e8d5648eSdan SessionChange *p; /* Used to iterate through changes */ 1330e8d5648eSdan 13314fccf43aSdan for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){ 1332e8d5648eSdan rc = sessionSelectBind(pStmt, nCol, abPK, p->aRecord, p->nRecord); 1333e8d5648eSdan if( rc==SQLITE_OK ){ 13344fccf43aSdan if( sqlite3_step(pStmt)==SQLITE_ROW ){ 13354fccf43aSdan int iCol; 1336e8d5648eSdan if( p->bInsert ){ 13374fccf43aSdan sessionAppendByte(&buf, SQLITE_INSERT, &rc); 1338e8d5648eSdan for(iCol=0; iCol<nCol; iCol++){ 13394fccf43aSdan sessionAppendCol(&buf, pStmt, iCol, &rc); 13404fccf43aSdan } 1341e8d5648eSdan }else{ 1342e8d5648eSdan sessionAppendUpdate(&buf, pStmt, p, abPK, &rc); 13434fccf43aSdan } 13444fccf43aSdan bNoop = 0; 1345e8d5648eSdan }else if( !p->bInsert ){ 13464fccf43aSdan /* A DELETE change */ 13474fccf43aSdan sessionAppendByte(&buf, SQLITE_DELETE, &rc); 13484fccf43aSdan sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc); 13494fccf43aSdan bNoop = 0; 13504fccf43aSdan } 13514fccf43aSdan rc = sqlite3_reset(pStmt); 13524fccf43aSdan } 13534fccf43aSdan } 1354e8d5648eSdan } 13554fccf43aSdan 13564fccf43aSdan sqlite3_finalize(pStmt); 13574fccf43aSdan 13584fccf43aSdan if( bNoop ){ 13594fccf43aSdan buf.nBuf = nRewind; 13604fccf43aSdan } 13614fccf43aSdan } 13624fccf43aSdan } 13634fccf43aSdan 13644fccf43aSdan if( rc==SQLITE_OK ){ 13654fccf43aSdan *pnChangeset = buf.nBuf; 13664fccf43aSdan *ppChangeset = buf.aBuf; 13674fccf43aSdan }else{ 13684fccf43aSdan sqlite3_free(buf.aBuf); 13694fccf43aSdan } 13704c220252Sdan 13714c220252Sdan sqlite3_mutex_leave(sqlite3_db_mutex(db)); 13724fccf43aSdan return rc; 13734fccf43aSdan } 13744fccf43aSdan 1375296c7658Sdan /* 1376296c7658Sdan ** Enable or disable the session object passed as the first argument. 1377296c7658Sdan */ 13784fccf43aSdan int sqlite3session_enable(sqlite3_session *pSession, int bEnable){ 13794c220252Sdan int ret; 13804c220252Sdan sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db)); 1381296c7658Sdan if( bEnable>=0 ){ 1382296c7658Sdan pSession->bEnable = bEnable; 13834fccf43aSdan } 13844c220252Sdan ret = pSession->bEnable; 13854c220252Sdan sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db)); 13864c220252Sdan return ret; 1387296c7658Sdan } 13884fccf43aSdan 13894fccf43aSdan /* 13904fccf43aSdan ** Create an iterator used to iterate through the contents of a changeset. 13914fccf43aSdan */ 13924fccf43aSdan int sqlite3changeset_start( 1393296c7658Sdan sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */ 1394296c7658Sdan int nChangeset, /* Size of buffer pChangeset in bytes */ 1395296c7658Sdan void *pChangeset /* Pointer to buffer containing changeset */ 13964fccf43aSdan ){ 13974fccf43aSdan sqlite3_changeset_iter *pRet; /* Iterator to return */ 13984fccf43aSdan int nByte; /* Number of bytes to allocate for iterator */ 13994fccf43aSdan 1400296c7658Sdan /* Zero the output variable in case an error occurs. */ 1401296c7658Sdan *pp = 0; 14024fccf43aSdan 1403296c7658Sdan /* Allocate and initialize the iterator structure. */ 14044fccf43aSdan nByte = sizeof(sqlite3_changeset_iter); 14054fccf43aSdan pRet = (sqlite3_changeset_iter *)sqlite3_malloc(nByte); 14064fccf43aSdan if( !pRet ) return SQLITE_NOMEM; 14074fccf43aSdan memset(pRet, 0, sizeof(sqlite3_changeset_iter)); 14084fccf43aSdan pRet->aChangeset = (u8 *)pChangeset; 14094fccf43aSdan pRet->nChangeset = nChangeset; 14104fccf43aSdan pRet->pNext = pRet->aChangeset; 14114fccf43aSdan 1412296c7658Sdan /* Populate the output variable and return success. */ 1413296c7658Sdan *pp = pRet; 14144fccf43aSdan return SQLITE_OK; 14154fccf43aSdan } 14164fccf43aSdan 1417296c7658Sdan /* 1418296c7658Sdan ** Deserialize a single record from a buffer in memory. See "RECORD FORMAT" 1419296c7658Sdan ** for details. 1420296c7658Sdan ** 1421296c7658Sdan ** When this function is called, *paChange points to the start of the record 1422296c7658Sdan ** to deserialize. Assuming no error occurs, *paChange is set to point to 1423296c7658Sdan ** one byte after the end of the same record before this function returns. 1424296c7658Sdan ** 1425296c7658Sdan ** If successful, each element of the apOut[] array (allocated by the caller) 1426296c7658Sdan ** is set to point to an sqlite3_value object containing the value read 1427296c7658Sdan ** from the corresponding position in the record. If that value is not 1428296c7658Sdan ** included in the record (i.e. because the record is part of an UPDATE change 1429296c7658Sdan ** and the field was not modified), the corresponding element of apOut[] is 1430296c7658Sdan ** set to NULL. 1431296c7658Sdan ** 1432296c7658Sdan ** It is the responsibility of the caller to free all sqlite_value structures 1433296c7658Sdan ** using sqlite3_free(). 1434296c7658Sdan ** 1435296c7658Sdan ** If an error occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned. 1436296c7658Sdan ** The apOut[] array may have been partially populated in this case. 1437296c7658Sdan */ 14384fccf43aSdan static int sessionReadRecord( 14394fccf43aSdan u8 **paChange, /* IN/OUT: Pointer to binary record */ 14404fccf43aSdan int nCol, /* Number of values in record */ 14414fccf43aSdan sqlite3_value **apOut /* Write values to this array */ 14424fccf43aSdan ){ 1443296c7658Sdan int i; /* Used to iterate through columns */ 1444296c7658Sdan u8 *aRec = *paChange; /* Cursor for the serialized record */ 14454fccf43aSdan 14464fccf43aSdan for(i=0; i<nCol; i++){ 1447296c7658Sdan int eType = *aRec++; /* Type of value (SQLITE_NULL, TEXT etc.) */ 144891ddd559Sdan assert( !apOut || apOut[i]==0 ); 14494fccf43aSdan if( eType ){ 145091ddd559Sdan if( apOut ){ 14514fccf43aSdan apOut[i] = sqlite3ValueNew(0); 14524fccf43aSdan if( !apOut[i] ) return SQLITE_NOMEM; 145391ddd559Sdan } 14544fccf43aSdan 14554fccf43aSdan if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ 14564fccf43aSdan int nByte; 14574fccf43aSdan int enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0); 14584fccf43aSdan aRec += sessionVarintGet(aRec, &nByte); 145991ddd559Sdan if( apOut ){ 14604fccf43aSdan sqlite3ValueSetStr(apOut[i], nByte, aRec, enc, SQLITE_STATIC); 146191ddd559Sdan } 14624fccf43aSdan aRec += nByte; 14634fccf43aSdan } 14644fccf43aSdan if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ 146591ddd559Sdan if( apOut ){ 14664fccf43aSdan sqlite3_int64 v = sessionGetI64(aRec); 14674fccf43aSdan if( eType==SQLITE_INTEGER ){ 14684fccf43aSdan sqlite3VdbeMemSetInt64(apOut[i], v); 14694fccf43aSdan }else{ 14704fccf43aSdan double d; 14714fccf43aSdan memcpy(&d, &i, 8); 14724fccf43aSdan sqlite3VdbeMemSetDouble(apOut[i], d); 14734fccf43aSdan } 14744fccf43aSdan } 147591ddd559Sdan aRec += 8; 147691ddd559Sdan } 14774fccf43aSdan } 14784fccf43aSdan } 14794fccf43aSdan 14804fccf43aSdan *paChange = aRec; 14814fccf43aSdan return SQLITE_OK; 14824fccf43aSdan } 14834fccf43aSdan 14844fccf43aSdan /* 14854fccf43aSdan ** Advance an iterator created by sqlite3changeset_start() to the next 14864fccf43aSdan ** change in the changeset. This function may return SQLITE_ROW, SQLITE_DONE 14874fccf43aSdan ** or SQLITE_CORRUPT. 14884fccf43aSdan ** 14894fccf43aSdan ** This function may not be called on iterators passed to a conflict handler 14904fccf43aSdan ** callback by changeset_apply(). 14914fccf43aSdan */ 14924fccf43aSdan int sqlite3changeset_next(sqlite3_changeset_iter *p){ 14934fccf43aSdan u8 *aChange; 14944fccf43aSdan int i; 14954fccf43aSdan u8 c; 14964fccf43aSdan 1497296c7658Sdan /* If the iterator is in the error-state, return immediately. */ 14984fccf43aSdan if( p->rc!=SQLITE_OK ) return p->rc; 14994fccf43aSdan 1500296c7658Sdan /* Free the current contents of p->apValue[]. */ 15014fccf43aSdan if( p->apValue ){ 15024fccf43aSdan for(i=0; i<p->nCol*2; i++){ 15034fccf43aSdan sqlite3ValueFree(p->apValue[i]); 15044fccf43aSdan } 15054fccf43aSdan memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2); 15064fccf43aSdan } 15074fccf43aSdan 15084fccf43aSdan /* If the iterator is already at the end of the changeset, return DONE. */ 15094fccf43aSdan if( p->pNext>=&p->aChangeset[p->nChangeset] ){ 15104fccf43aSdan return SQLITE_DONE; 15114fccf43aSdan } 15124fccf43aSdan aChange = p->pNext; 15134fccf43aSdan 15144fccf43aSdan c = *(aChange++); 15154fccf43aSdan if( c=='T' ){ 15164fccf43aSdan int nByte; /* Bytes to allocate for apValue */ 15174fccf43aSdan aChange += sessionVarintGet(aChange, &p->nCol); 15184fccf43aSdan p->zTab = (char *)aChange; 15194fccf43aSdan aChange += (strlen((char *)aChange) + 1); 15204fccf43aSdan p->op = *(aChange++); 15214fccf43aSdan sqlite3_free(p->apValue); 15224fccf43aSdan nByte = sizeof(sqlite3_value *) * p->nCol * 2; 15234fccf43aSdan p->apValue = (sqlite3_value **)sqlite3_malloc(nByte); 15244fccf43aSdan if( !p->apValue ){ 15254fccf43aSdan return (p->rc = SQLITE_NOMEM); 15264fccf43aSdan } 15274fccf43aSdan memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2); 15284fccf43aSdan }else{ 15294fccf43aSdan p->op = c; 15304fccf43aSdan } 15314fccf43aSdan if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){ 15324fccf43aSdan return (p->rc = SQLITE_CORRUPT); 15334fccf43aSdan } 15344fccf43aSdan 15354fccf43aSdan /* If this is an UPDATE or DELETE, read the old.* record. */ 15364fccf43aSdan if( p->op!=SQLITE_INSERT ){ 15374fccf43aSdan p->rc = sessionReadRecord(&aChange, p->nCol, p->apValue); 15384fccf43aSdan if( p->rc!=SQLITE_OK ) return p->rc; 15394fccf43aSdan } 15404fccf43aSdan 15414fccf43aSdan /* If this is an INSERT or UPDATE, read the new.* record. */ 15424fccf43aSdan if( p->op!=SQLITE_DELETE ){ 15434fccf43aSdan p->rc = sessionReadRecord(&aChange, p->nCol, &p->apValue[p->nCol]); 15444fccf43aSdan if( p->rc!=SQLITE_OK ) return p->rc; 15454fccf43aSdan } 15464fccf43aSdan 15474fccf43aSdan p->pNext = aChange; 15484fccf43aSdan return SQLITE_ROW; 15494fccf43aSdan } 15504fccf43aSdan 15514fccf43aSdan /* 15524fccf43aSdan ** The following three functions extract information on the current change 15534fccf43aSdan ** from a changeset iterator. They may only be called after changeset_next() 15544fccf43aSdan ** has returned SQLITE_ROW. 15554fccf43aSdan */ 15564fccf43aSdan int sqlite3changeset_op( 1557296c7658Sdan sqlite3_changeset_iter *pIter, /* Iterator handle */ 15584fccf43aSdan const char **pzTab, /* OUT: Pointer to table name */ 15594fccf43aSdan int *pnCol, /* OUT: Number of columns in table */ 15604fccf43aSdan int *pOp /* OUT: SQLITE_INSERT, DELETE or UPDATE */ 15614fccf43aSdan ){ 15624fccf43aSdan *pOp = pIter->op; 15634fccf43aSdan *pnCol = pIter->nCol; 15644fccf43aSdan *pzTab = pIter->zTab; 15654fccf43aSdan return SQLITE_OK; 15664fccf43aSdan } 15674fccf43aSdan 1568296c7658Sdan /* 1569296c7658Sdan ** This function may only be called while the iterator is pointing to an 1570296c7658Sdan ** SQLITE_UPDATE or SQLITE_DELETE change (see sqlite3changeset_op()). 1571296c7658Sdan ** Otherwise, SQLITE_MISUSE is returned. 1572296c7658Sdan ** 1573296c7658Sdan ** It sets *ppValue to point to an sqlite3_value structure containing the 1574296c7658Sdan ** iVal'th value in the old.* record. Or, if that particular value is not 1575296c7658Sdan ** included in the record (because the change is an UPDATE and the field 1576296c7658Sdan ** was not modified and is not a PK column), set *ppValue to NULL. 1577296c7658Sdan ** 1578296c7658Sdan ** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is 1579296c7658Sdan ** not modified. Otherwise, SQLITE_OK. 1580296c7658Sdan */ 15814fccf43aSdan int sqlite3changeset_old( 1582296c7658Sdan sqlite3_changeset_iter *pIter, /* Changeset iterator */ 1583296c7658Sdan int iVal, /* Index of old.* value to retrieve */ 15844fccf43aSdan sqlite3_value **ppValue /* OUT: Old value (or NULL pointer) */ 15854fccf43aSdan ){ 1586d5f0767cSdan if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_DELETE ){ 1587d5f0767cSdan return SQLITE_MISUSE; 1588d5f0767cSdan } 15894fccf43aSdan if( iVal<0 || iVal>=pIter->nCol ){ 15904fccf43aSdan return SQLITE_RANGE; 15914fccf43aSdan } 15924fccf43aSdan *ppValue = pIter->apValue[iVal]; 15934fccf43aSdan return SQLITE_OK; 15944fccf43aSdan } 15954fccf43aSdan 1596296c7658Sdan /* 1597296c7658Sdan ** This function may only be called while the iterator is pointing to an 1598296c7658Sdan ** SQLITE_UPDATE or SQLITE_INSERT change (see sqlite3changeset_op()). 1599296c7658Sdan ** Otherwise, SQLITE_MISUSE is returned. 1600296c7658Sdan ** 1601296c7658Sdan ** It sets *ppValue to point to an sqlite3_value structure containing the 1602296c7658Sdan ** iVal'th value in the new.* record. Or, if that particular value is not 1603296c7658Sdan ** included in the record (because the change is an UPDATE and the field 1604296c7658Sdan ** was not modified), set *ppValue to NULL. 1605296c7658Sdan ** 1606296c7658Sdan ** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is 1607296c7658Sdan ** not modified. Otherwise, SQLITE_OK. 1608296c7658Sdan */ 16094fccf43aSdan int sqlite3changeset_new( 1610296c7658Sdan sqlite3_changeset_iter *pIter, /* Changeset iterator */ 1611296c7658Sdan int iVal, /* Index of new.* value to retrieve */ 16124fccf43aSdan sqlite3_value **ppValue /* OUT: New value (or NULL pointer) */ 16134fccf43aSdan ){ 1614d5f0767cSdan if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_INSERT ){ 1615d5f0767cSdan return SQLITE_MISUSE; 1616d5f0767cSdan } 16174fccf43aSdan if( iVal<0 || iVal>=pIter->nCol ){ 16184fccf43aSdan return SQLITE_RANGE; 16194fccf43aSdan } 16204fccf43aSdan *ppValue = pIter->apValue[pIter->nCol+iVal]; 16214fccf43aSdan return SQLITE_OK; 16224fccf43aSdan } 16234fccf43aSdan 1624296c7658Sdan /* 1625296c7658Sdan ** This function may only be called with a changeset iterator that has been 1626296c7658Sdan ** passed to an SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT 1627296c7658Sdan ** conflict-handler function. Otherwise, SQLITE_MISUSE is returned. 1628296c7658Sdan ** 1629296c7658Sdan ** If successful, *ppValue is set to point to an sqlite3_value structure 1630296c7658Sdan ** containing the iVal'th value of the conflicting record. 1631296c7658Sdan ** 1632296c7658Sdan ** If value iVal is out-of-range or some other error occurs, an SQLite error 1633296c7658Sdan ** code is returned. Otherwise, SQLITE_OK. 1634296c7658Sdan */ 1635d5f0767cSdan int sqlite3changeset_conflict( 1636296c7658Sdan sqlite3_changeset_iter *pIter, /* Changeset iterator */ 1637296c7658Sdan int iVal, /* Index of conflict record value to fetch */ 1638d5f0767cSdan sqlite3_value **ppValue /* OUT: Value from conflicting row */ 1639d5f0767cSdan ){ 1640d5f0767cSdan if( !pIter->pConflict ){ 1641d5f0767cSdan return SQLITE_MISUSE; 1642d5f0767cSdan } 1643d5f0767cSdan if( iVal<0 || iVal>=sqlite3_column_count(pIter->pConflict) ){ 1644d5f0767cSdan return SQLITE_RANGE; 1645d5f0767cSdan } 1646d5f0767cSdan *ppValue = sqlite3_column_value(pIter->pConflict, iVal); 1647d5f0767cSdan return SQLITE_OK; 1648d5f0767cSdan } 1649d5f0767cSdan 16504fccf43aSdan /* 16514fccf43aSdan ** Finalize an iterator allocated with sqlite3changeset_start(). 16524fccf43aSdan ** 16534fccf43aSdan ** This function may not be called on iterators passed to a conflict handler 16544fccf43aSdan ** callback by changeset_apply(). 16554fccf43aSdan */ 16564fccf43aSdan int sqlite3changeset_finalize(sqlite3_changeset_iter *p){ 1657296c7658Sdan int i; /* Used to iterate through p->apValue[] */ 1658296c7658Sdan int rc = p->rc; /* Return code */ 16594fccf43aSdan for(i=0; i<p->nCol*2; i++) sqlite3ValueFree(p->apValue[i]); 16604fccf43aSdan sqlite3_free(p->apValue); 16614fccf43aSdan sqlite3_free(p); 16624fccf43aSdan return rc; 16634fccf43aSdan } 16644fccf43aSdan 166591ddd559Sdan /* 166691ddd559Sdan ** Invert a changeset object. 166791ddd559Sdan */ 166891ddd559Sdan int sqlite3changeset_invert( 166991ddd559Sdan int nChangeset, /* Number of bytes in input */ 167091ddd559Sdan void *pChangeset, /* Input changeset */ 167191ddd559Sdan int *pnInverted, /* OUT: Number of bytes in output changeset */ 167291ddd559Sdan void **ppInverted /* OUT: Inverse of pChangeset */ 167391ddd559Sdan ){ 167491ddd559Sdan u8 *aOut; 167591ddd559Sdan u8 *aIn; 167691ddd559Sdan int i; 167791ddd559Sdan int nCol = 0; 167891ddd559Sdan 167991ddd559Sdan /* Zero the output variables in case an error occurs. */ 168091ddd559Sdan *ppInverted = 0; 168191ddd559Sdan *pnInverted = 0; 168291ddd559Sdan if( nChangeset==0 ) return SQLITE_OK; 168391ddd559Sdan 168491ddd559Sdan aOut = (u8 *)sqlite3_malloc(nChangeset); 168591ddd559Sdan if( !aOut ) return SQLITE_NOMEM; 168691ddd559Sdan aIn = (u8 *)pChangeset; 168791ddd559Sdan 168891ddd559Sdan i = 0; 168991ddd559Sdan while( i<nChangeset ){ 169091ddd559Sdan u8 eType = aIn[i]; 169191ddd559Sdan switch( eType ){ 169291ddd559Sdan case 'T': { 169391ddd559Sdan int nByte = 1 + sessionVarintGet(&aIn[i+1], &nCol); 169491ddd559Sdan nByte += 1 + strlen((char *)&aIn[i+nByte]); 169591ddd559Sdan memcpy(&aOut[i], &aIn[i], nByte); 169691ddd559Sdan i += nByte; 169791ddd559Sdan break; 169891ddd559Sdan } 169991ddd559Sdan 170091ddd559Sdan case SQLITE_INSERT: 170191ddd559Sdan case SQLITE_DELETE: { 170291ddd559Sdan int nByte; 170391ddd559Sdan u8 *aEnd = &aIn[i+1]; 170491ddd559Sdan 170591ddd559Sdan sessionReadRecord(&aEnd, nCol, 0); 170691ddd559Sdan aOut[i] = (eType==SQLITE_DELETE ? SQLITE_INSERT : SQLITE_DELETE); 170791ddd559Sdan nByte = aEnd - &aIn[i+1]; 170891ddd559Sdan memcpy(&aOut[i+1], &aIn[i+1], nByte); 170991ddd559Sdan i += 1 + nByte; 171091ddd559Sdan break; 171191ddd559Sdan } 171291ddd559Sdan 171391ddd559Sdan case SQLITE_UPDATE: { 171491ddd559Sdan int nByte1; /* Size of old.* record in bytes */ 171591ddd559Sdan int nByte2; /* Size of new.* record in bytes */ 171691ddd559Sdan u8 *aEnd = &aIn[i+1]; 171791ddd559Sdan 171891ddd559Sdan sessionReadRecord(&aEnd, nCol, 0); 171991ddd559Sdan nByte1 = aEnd - &aIn[i+1]; 172091ddd559Sdan sessionReadRecord(&aEnd, nCol, 0); 172191ddd559Sdan nByte2 = aEnd - &aIn[i+1] - nByte1; 172291ddd559Sdan 172391ddd559Sdan aOut[i] = SQLITE_UPDATE; 172491ddd559Sdan memcpy(&aOut[i+1], &aIn[i+1+nByte1], nByte2); 172591ddd559Sdan memcpy(&aOut[i+1+nByte2], &aIn[i+1], nByte1); 172691ddd559Sdan 172791ddd559Sdan i += 1 + nByte1 + nByte2; 172891ddd559Sdan break; 172991ddd559Sdan } 173091ddd559Sdan 173191ddd559Sdan default: 173291ddd559Sdan sqlite3_free(aOut); 173391ddd559Sdan return SQLITE_CORRUPT; 173491ddd559Sdan } 173591ddd559Sdan } 173691ddd559Sdan 173791ddd559Sdan *pnInverted = nChangeset; 173891ddd559Sdan *ppInverted = (void *)aOut; 173991ddd559Sdan return SQLITE_OK; 174091ddd559Sdan } 174191ddd559Sdan 17420c698471Sdan typedef struct SessionApplyCtx SessionApplyCtx; 17430c698471Sdan struct SessionApplyCtx { 17440c698471Sdan sqlite3 *db; 17450c698471Sdan sqlite3_stmt *pDelete; /* DELETE statement */ 17460c698471Sdan sqlite3_stmt *pUpdate; /* DELETE statement */ 17470c698471Sdan sqlite3_stmt *pInsert; /* INSERT statement */ 17480c698471Sdan sqlite3_stmt *pSelect; /* SELECT statement */ 17490c698471Sdan int nCol; /* Size of azCol[] and abPK[] arrays */ 17500c698471Sdan const char **azCol; /* Array of column names */ 17510c698471Sdan u8 *abPK; /* Boolean array - true if column is in PK */ 17520c698471Sdan }; 17530c698471Sdan 1754d5f0767cSdan /* 1755d5f0767cSdan ** Formulate a statement to DELETE a row from database db. Assuming a table 1756d5f0767cSdan ** structure like this: 1757d5f0767cSdan ** 1758d5f0767cSdan ** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c)); 1759d5f0767cSdan ** 1760d5f0767cSdan ** The DELETE statement looks like this: 1761d5f0767cSdan ** 1762d5f0767cSdan ** DELETE FROM x WHERE a = :1 AND c = :3 AND :5 OR (b IS :2 AND d IS :4) 1763d5f0767cSdan ** 1764d5f0767cSdan ** Variable :5 (nCol+1) is a boolean. It should be set to 0 if we require 1765d5f0767cSdan ** matching b and d values, or 1 otherwise. The second case comes up if the 1766d5f0767cSdan ** conflict handler is invoked with NOTFOUND and returns CHANGESET_REPLACE. 1767296c7658Sdan ** 1768296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pDelete is left 1769296c7658Sdan ** pointing to the prepared version of the SQL statement. 1770d5f0767cSdan */ 1771d5f0767cSdan static int sessionDeleteRow( 1772d5f0767cSdan sqlite3 *db, /* Database handle */ 1773d5f0767cSdan const char *zTab, /* Table name */ 17740c698471Sdan SessionApplyCtx *p /* Session changeset-apply context */ 1775d5f0767cSdan ){ 1776296c7658Sdan int i; 1777296c7658Sdan const char *zSep = ""; 1778d5f0767cSdan int rc = SQLITE_OK; 1779d5f0767cSdan SessionBuffer buf = {0, 0, 0}; 1780*7cf7df7dSdan int nPk = 0; 1781d5f0767cSdan 1782d5f0767cSdan sessionAppendStr(&buf, "DELETE FROM ", &rc); 1783d5f0767cSdan sessionAppendIdent(&buf, zTab, &rc); 1784296c7658Sdan sessionAppendStr(&buf, " WHERE ", &rc); 1785296c7658Sdan 1786296c7658Sdan for(i=0; i<p->nCol; i++){ 1787296c7658Sdan if( p->abPK[i] ){ 1788*7cf7df7dSdan nPk++; 1789296c7658Sdan sessionAppendStr(&buf, zSep, &rc); 1790296c7658Sdan sessionAppendIdent(&buf, p->azCol[i], &rc); 1791296c7658Sdan sessionAppendStr(&buf, " = ?", &rc); 1792296c7658Sdan sessionAppendInteger(&buf, i+1, &rc); 1793296c7658Sdan zSep = " AND "; 1794296c7658Sdan } 1795296c7658Sdan } 1796296c7658Sdan 1797*7cf7df7dSdan if( nPk<p->nCol ){ 1798296c7658Sdan sessionAppendStr(&buf, " AND (?", &rc); 1799296c7658Sdan sessionAppendInteger(&buf, p->nCol+1, &rc); 1800296c7658Sdan sessionAppendStr(&buf, " OR ", &rc); 1801296c7658Sdan 1802296c7658Sdan zSep = ""; 1803296c7658Sdan for(i=0; i<p->nCol; i++){ 1804296c7658Sdan if( !p->abPK[i] ){ 1805296c7658Sdan sessionAppendStr(&buf, zSep, &rc); 1806296c7658Sdan sessionAppendIdent(&buf, p->azCol[i], &rc); 1807296c7658Sdan sessionAppendStr(&buf, " IS ?", &rc); 1808296c7658Sdan sessionAppendInteger(&buf, i+1, &rc); 1809296c7658Sdan zSep = "AND "; 1810296c7658Sdan } 1811296c7658Sdan } 1812296c7658Sdan sessionAppendStr(&buf, ")", &rc); 1813*7cf7df7dSdan } 1814d5f0767cSdan 1815d5f0767cSdan if( rc==SQLITE_OK ){ 18160c698471Sdan rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pDelete, 0); 1817d5f0767cSdan } 1818d5f0767cSdan sqlite3_free(buf.aBuf); 1819d5f0767cSdan 1820d5f0767cSdan return rc; 1821d5f0767cSdan } 1822d5f0767cSdan 1823d5f0767cSdan /* 1824d5f0767cSdan ** Formulate and prepare a statement to UPDATE a row from database db. 1825d5f0767cSdan ** Assuming a table structure like this: 1826d5f0767cSdan ** 1827d5f0767cSdan ** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c)); 1828d5f0767cSdan ** 1829d5f0767cSdan ** The UPDATE statement looks like this: 1830d5f0767cSdan ** 1831d5f0767cSdan ** UPDATE x SET 1832d5f0767cSdan ** a = CASE WHEN ?2 THEN ?3 ELSE a END, 1833d5f0767cSdan ** b = CASE WHEN ?5 THEN ?6 ELSE a END, 1834d5f0767cSdan ** c = CASE WHEN ?8 THEN ?9 ELSE a END, 1835d5f0767cSdan ** d = CASE WHEN ?11 THEN ?12 ELSE a END 1836d5f0767cSdan ** WHERE a = ?1 AND c = ?7 AND (?13 OR 1837d5f0767cSdan ** (?5==0 OR b IS ?4) AND (?11==0 OR b IS ?10) AND 1838d5f0767cSdan ** ) 1839d5f0767cSdan ** 1840d5f0767cSdan ** For each column in the table, there are three variables to bind: 1841d5f0767cSdan ** 1842d5f0767cSdan ** ?(i*3+1) The old.* value of the column, if any. 1843d5f0767cSdan ** ?(i*3+2) A boolean flag indicating that the value is being modified. 1844d5f0767cSdan ** ?(i*3+3) The new.* value of the column, if any. 1845d5f0767cSdan ** 1846d5f0767cSdan ** Also, a boolean flag that, if set to true, causes the statement to update 1847d5f0767cSdan ** a row even if the non-PK values do not match. This is required if the 1848d5f0767cSdan ** conflict-handler is invoked with CHANGESET_DATA and returns 1849d5f0767cSdan ** CHANGESET_REPLACE. This is variable "?(nCol*3+1)". 1850d5f0767cSdan ** 1851296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left 1852296c7658Sdan ** pointing to the prepared version of the SQL statement. 1853d5f0767cSdan */ 1854d5f0767cSdan static int sessionUpdateRow( 1855d5f0767cSdan sqlite3 *db, /* Database handle */ 1856d5f0767cSdan const char *zTab, /* Table name */ 18570c698471Sdan SessionApplyCtx *p /* Session changeset-apply context */ 1858d5f0767cSdan ){ 1859d5f0767cSdan int rc = SQLITE_OK; 1860d5f0767cSdan int i; 1861d5f0767cSdan const char *zSep = ""; 1862d5f0767cSdan SessionBuffer buf = {0, 0, 0}; 1863d5f0767cSdan 1864d5f0767cSdan /* Append "UPDATE tbl SET " */ 1865d5f0767cSdan sessionAppendStr(&buf, "UPDATE ", &rc); 1866d5f0767cSdan sessionAppendIdent(&buf, zTab, &rc); 1867d5f0767cSdan sessionAppendStr(&buf, " SET ", &rc); 1868d5f0767cSdan 1869d5f0767cSdan /* Append the assignments */ 18700c698471Sdan for(i=0; i<p->nCol; i++){ 1871d5f0767cSdan sessionAppendStr(&buf, zSep, &rc); 18720c698471Sdan sessionAppendIdent(&buf, p->azCol[i], &rc); 1873d5f0767cSdan sessionAppendStr(&buf, " = CASE WHEN ?", &rc); 1874d5f0767cSdan sessionAppendInteger(&buf, i*3+2, &rc); 1875d5f0767cSdan sessionAppendStr(&buf, " THEN ?", &rc); 1876d5f0767cSdan sessionAppendInteger(&buf, i*3+3, &rc); 1877d5f0767cSdan sessionAppendStr(&buf, " ELSE ", &rc); 18780c698471Sdan sessionAppendIdent(&buf, p->azCol[i], &rc); 1879d5f0767cSdan sessionAppendStr(&buf, " END", &rc); 1880d5f0767cSdan zSep = ", "; 1881d5f0767cSdan } 1882d5f0767cSdan 1883d5f0767cSdan /* Append the PK part of the WHERE clause */ 1884d5f0767cSdan sessionAppendStr(&buf, " WHERE ", &rc); 18850c698471Sdan for(i=0; i<p->nCol; i++){ 18860c698471Sdan if( p->abPK[i] ){ 18870c698471Sdan sessionAppendIdent(&buf, p->azCol[i], &rc); 1888d5f0767cSdan sessionAppendStr(&buf, " = ?", &rc); 1889d5f0767cSdan sessionAppendInteger(&buf, i*3+1, &rc); 1890d5f0767cSdan sessionAppendStr(&buf, " AND ", &rc); 1891d5f0767cSdan } 1892d5f0767cSdan } 1893d5f0767cSdan 1894d5f0767cSdan /* Append the non-PK part of the WHERE clause */ 1895d5f0767cSdan sessionAppendStr(&buf, " (?", &rc); 18960c698471Sdan sessionAppendInteger(&buf, p->nCol*3+1, &rc); 1897d5f0767cSdan sessionAppendStr(&buf, " OR 1", &rc); 18980c698471Sdan for(i=0; i<p->nCol; i++){ 18990c698471Sdan if( !p->abPK[i] ){ 1900d5f0767cSdan sessionAppendStr(&buf, " AND (?", &rc); 1901d5f0767cSdan sessionAppendInteger(&buf, i*3+2, &rc); 1902d5f0767cSdan sessionAppendStr(&buf, "=0 OR ", &rc); 19030c698471Sdan sessionAppendIdent(&buf, p->azCol[i], &rc); 1904d5f0767cSdan sessionAppendStr(&buf, " IS ?", &rc); 1905d5f0767cSdan sessionAppendInteger(&buf, i*3+1, &rc); 1906d5f0767cSdan sessionAppendStr(&buf, ")", &rc); 1907d5f0767cSdan } 1908d5f0767cSdan } 1909d5f0767cSdan sessionAppendStr(&buf, ")", &rc); 1910d5f0767cSdan 1911d5f0767cSdan if( rc==SQLITE_OK ){ 19120c698471Sdan rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0); 1913d5f0767cSdan } 1914d5f0767cSdan sqlite3_free(buf.aBuf); 1915d5f0767cSdan 1916d5f0767cSdan return rc; 1917d5f0767cSdan } 1918d5f0767cSdan 1919296c7658Sdan /* 1920296c7658Sdan ** Formulate and prepare an SQL statement to query table zTab by primary 1921296c7658Sdan ** key. Assuming the following table structure: 1922296c7658Sdan ** 1923296c7658Sdan ** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c)); 1924296c7658Sdan ** 1925296c7658Sdan ** The SELECT statement looks like this: 1926296c7658Sdan ** 1927296c7658Sdan ** SELECT * FROM x WHERE a = ?1 AND c = ?3 1928296c7658Sdan ** 1929296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pSelect is left 1930296c7658Sdan ** pointing to the prepared version of the SQL statement. 1931296c7658Sdan */ 1932d5f0767cSdan static int sessionSelectRow( 1933d5f0767cSdan sqlite3 *db, /* Database handle */ 1934d5f0767cSdan const char *zTab, /* Table name */ 19350c698471Sdan SessionApplyCtx *p /* Session changeset-apply context */ 1936d5f0767cSdan ){ 1937e8d5648eSdan return sessionSelectStmt(db, zTab, p->nCol, p->azCol, p->abPK, &p->pSelect); 1938d5f0767cSdan } 1939d5f0767cSdan 1940296c7658Sdan /* 1941296c7658Sdan ** Formulate and prepare an INSERT statement to add a record to table zTab. 1942296c7658Sdan ** For example: 1943296c7658Sdan ** 1944296c7658Sdan ** INSERT INTO main."zTab" VALUES(?1, ?2, ?3 ...); 1945296c7658Sdan ** 1946296c7658Sdan ** If successful, SQLITE_OK is returned and SessionApplyCtx.pInsert is left 1947296c7658Sdan ** pointing to the prepared version of the SQL statement. 1948296c7658Sdan */ 19490c698471Sdan static int sessionInsertRow( 19500c698471Sdan sqlite3 *db, /* Database handle */ 19510c698471Sdan const char *zTab, /* Table name */ 19520c698471Sdan SessionApplyCtx *p /* Session changeset-apply context */ 19530c698471Sdan ){ 19540c698471Sdan int rc = SQLITE_OK; 19550c698471Sdan int i; 19560c698471Sdan SessionBuffer buf = {0, 0, 0}; 19570c698471Sdan 19580c698471Sdan sessionAppendStr(&buf, "INSERT INTO main.", &rc); 19590c698471Sdan sessionAppendIdent(&buf, zTab, &rc); 19600c698471Sdan sessionAppendStr(&buf, " VALUES(?", &rc); 19610c698471Sdan for(i=1; i<p->nCol; i++){ 19620c698471Sdan sessionAppendStr(&buf, ", ?", &rc); 19630c698471Sdan } 19640c698471Sdan sessionAppendStr(&buf, ")", &rc); 19650c698471Sdan 19660c698471Sdan if( rc==SQLITE_OK ){ 19670c698471Sdan rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pInsert, 0); 19680c698471Sdan } 19690c698471Sdan sqlite3_free(buf.aBuf); 19700c698471Sdan return rc; 19710c698471Sdan } 19720c698471Sdan 1973296c7658Sdan /* 1974296c7658Sdan ** SQL statement pSelect is as generated by the sessionSelectRow() function. 1975296c7658Sdan ** This function binds the primary key values from the change that changeset 1976296c7658Sdan ** iterator pIter points to to the SELECT and attempts to seek to the table 1977296c7658Sdan ** entry. If a row is found, the SELECT statement left pointing at the row 1978296c7658Sdan ** and SQLITE_ROW is returned. Otherwise, if no row is found and no error 1979296c7658Sdan ** has occured, the statement is reset and SQLITE_OK is returned. If an 1980296c7658Sdan ** error occurs, an SQLite error code is returned. 1981296c7658Sdan ** 1982296c7658Sdan ** If the iterator currently points to an INSERT record, bind values from the 1983296c7658Sdan ** new.* record to the SELECT statement. Or, if it points to a DELETE, bind 1984296c7658Sdan ** values from the old.* record. If the changeset iterator points to an 1985296c7658Sdan ** UPDATE, bind values from the new.* record, but use old.* values in place 1986296c7658Sdan ** of any undefined new.* values. 1987296c7658Sdan */ 19880c698471Sdan static int sessionSeekToRow( 198937f133ecSdan sqlite3 *db, /* Database handle */ 199037f133ecSdan sqlite3_changeset_iter *pIter, /* Changeset iterator */ 199137f133ecSdan u8 *abPK, /* Primary key flags array */ 19920c698471Sdan sqlite3_stmt *pSelect /* SELECT statement from sessionSelectRow() */ 199337f133ecSdan ){ 1994296c7658Sdan int rc = SQLITE_OK; /* Return code */ 1995296c7658Sdan int i; /* Used to iterate through table columns */ 1996296c7658Sdan int nCol; /* Number of columns in table */ 1997296c7658Sdan int op; /* Changset operation (SQLITE_UPDATE etc.) */ 1998296c7658Sdan const char *zDummy; /* Unused */ 199937f133ecSdan 200037f133ecSdan sqlite3changeset_op(pIter, &zDummy, &nCol, &op); 20010c698471Sdan 20020c698471Sdan for(i=0; rc==SQLITE_OK && i<nCol; i++){ 20030c698471Sdan if( abPK[i] ){ 20040c698471Sdan sqlite3_value *pVal = 0; 20050c698471Sdan if( op!=SQLITE_DELETE ){ 20060c698471Sdan rc = sqlite3changeset_new(pIter, i, &pVal); 20070c698471Sdan } 20080c698471Sdan if( pVal==0 ){ 20090c698471Sdan rc = sqlite3changeset_old(pIter, i, &pVal); 20100c698471Sdan } 20110c698471Sdan if( rc==SQLITE_OK ){ 20120c698471Sdan rc = sqlite3_bind_value(pSelect, i+1, pVal); 20130c698471Sdan } 20140c698471Sdan } 20150c698471Sdan } 20160c698471Sdan 20170c698471Sdan if( rc==SQLITE_OK ){ 20180c698471Sdan rc = sqlite3_step(pSelect); 20190c698471Sdan if( rc!=SQLITE_ROW ) rc = sqlite3_reset(pSelect); 20200c698471Sdan } 20210c698471Sdan 20220c698471Sdan return rc; 20230c698471Sdan } 20240c698471Sdan 2025296c7658Sdan /* 2026296c7658Sdan ** Invoke the conflict handler for the change that the changeset iterator 2027296c7658Sdan ** currently points to. 2028296c7658Sdan ** 2029296c7658Sdan ** Argument eType must be either CHANGESET_DATA or CHANGESET_CONFLICT. 2030296c7658Sdan ** If argument pbReplace is NULL, then the type of conflict handler invoked 2031296c7658Sdan ** depends solely on eType, as follows: 2032296c7658Sdan ** 2033296c7658Sdan ** eType value Value passed to xConflict 2034296c7658Sdan ** ------------------------------------------------- 2035296c7658Sdan ** CHANGESET_DATA CHANGESET_NOTFOUND 2036296c7658Sdan ** CHANGESET_CONFLICT CHANGESET_CONSTRAINT 2037296c7658Sdan ** 2038296c7658Sdan ** Or, if pbReplace is not NULL, then an attempt is made to find an existing 2039296c7658Sdan ** record with the same primary key as the record about to be deleted, updated 2040296c7658Sdan ** or inserted. If such a record can be found, it is available to the conflict 2041296c7658Sdan ** handler as the "conflicting" record. In this case the type of conflict 2042296c7658Sdan ** handler invoked is as follows: 2043296c7658Sdan ** 2044296c7658Sdan ** eType value PK Record found? Value passed to xConflict 2045296c7658Sdan ** ---------------------------------------------------------------- 2046296c7658Sdan ** CHANGESET_DATA Yes CHANGESET_DATA 2047296c7658Sdan ** CHANGESET_DATA No CHANGESET_NOTFOUND 2048296c7658Sdan ** CHANGESET_CONFLICT Yes CHANGESET_CONFLICT 2049296c7658Sdan ** CHANGESET_CONFLICT No CHANGESET_CONSTRAINT 2050296c7658Sdan ** 2051296c7658Sdan ** If pbReplace is not NULL, and a record with a matching PK is found, and 2052296c7658Sdan ** the conflict handler function returns SQLITE_CHANGESET_REPLACE, *pbReplace 2053296c7658Sdan ** is set to non-zero before returning SQLITE_OK. 2054296c7658Sdan ** 2055296c7658Sdan ** If the conflict handler returns SQLITE_CHANGESET_ABORT, SQLITE_ABORT is 2056296c7658Sdan ** returned. Or, if the conflict handler returns an invalid value, 2057296c7658Sdan ** SQLITE_MISUSE. If the conflict handler returns SQLITE_CHANGESET_OMIT, 2058296c7658Sdan ** this function returns SQLITE_OK. 2059296c7658Sdan */ 20600c698471Sdan static int sessionConflictHandler( 2061296c7658Sdan int eType, /* Either CHANGESET_DATA or CONFLICT */ 2062296c7658Sdan SessionApplyCtx *p, /* changeset_apply() context */ 20630c698471Sdan sqlite3_changeset_iter *pIter, /* Changeset iterator */ 20640c698471Sdan int(*xConflict)(void *, int, sqlite3_changeset_iter*), 2065296c7658Sdan void *pCtx, /* First argument for conflict handler */ 2066296c7658Sdan int *pbReplace /* OUT: Set to true if PK row is found */ 20670c698471Sdan ){ 2068296c7658Sdan int res; /* Value returned by conflict handler */ 20690c698471Sdan int rc; 20700c698471Sdan int nCol; 20710c698471Sdan int op; 20720c698471Sdan const char *zDummy; 20730c698471Sdan 20740c698471Sdan sqlite3changeset_op(pIter, &zDummy, &nCol, &op); 20750c698471Sdan 20760c698471Sdan assert( eType==SQLITE_CHANGESET_CONFLICT || eType==SQLITE_CHANGESET_DATA ); 20770c698471Sdan assert( SQLITE_CHANGESET_CONFLICT+1==SQLITE_CHANGESET_CONSTRAINT ); 20780c698471Sdan assert( SQLITE_CHANGESET_DATA+1==SQLITE_CHANGESET_NOTFOUND ); 207937f133ecSdan 208037f133ecSdan /* Bind the new.* PRIMARY KEY values to the SELECT statement. */ 20810c698471Sdan if( pbReplace ){ 20820c698471Sdan rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect); 20830c698471Sdan }else{ 20840c698471Sdan rc = SQLITE_DONE; 20850c698471Sdan } 20860c698471Sdan 20870c698471Sdan if( rc==SQLITE_ROW ){ 20880c698471Sdan /* There exists another row with the new.* primary key. */ 20890c698471Sdan pIter->pConflict = p->pSelect; 20900c698471Sdan res = xConflict(pCtx, eType, pIter); 20910c698471Sdan pIter->pConflict = 0; 20920c698471Sdan rc = sqlite3_reset(p->pSelect); 20930c698471Sdan }else{ 20940c698471Sdan /* No other row with the new.* primary key. */ 20950c698471Sdan rc = sqlite3_reset(p->pSelect); 20960c698471Sdan if( rc==SQLITE_OK ){ 20970c698471Sdan res = xConflict(pCtx, eType+1, pIter); 20980c698471Sdan if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE; 209937f133ecSdan } 210037f133ecSdan } 210137f133ecSdan 210237f133ecSdan if( rc==SQLITE_OK ){ 21030c698471Sdan switch( res ){ 21040c698471Sdan case SQLITE_CHANGESET_REPLACE: 21050c698471Sdan if( pbReplace ) *pbReplace = 1; 21060c698471Sdan break; 21070c698471Sdan 21080c698471Sdan case SQLITE_CHANGESET_OMIT: 21090c698471Sdan break; 21100c698471Sdan 21110c698471Sdan case SQLITE_CHANGESET_ABORT: 21120c698471Sdan rc = SQLITE_ABORT; 21130c698471Sdan break; 21140c698471Sdan 21150c698471Sdan default: 21160c698471Sdan rc = SQLITE_MISUSE; 21170c698471Sdan break; 21180c698471Sdan } 21190c698471Sdan } 21200c698471Sdan 21210c698471Sdan return rc; 21220c698471Sdan } 21230c698471Sdan 2124296c7658Sdan /* 2125296c7658Sdan ** Attempt to apply the change that the iterator passed as the first argument 2126296c7658Sdan ** currently points to to the database. If a conflict is encountered, invoke 2127296c7658Sdan ** the conflict handler callback. 2128296c7658Sdan ** 2129296c7658Sdan ** If argument pbRetry is NULL, then ignore any CHANGESET_DATA conflict. If 2130296c7658Sdan ** one is encountered, update or delete the row with the matching primary key 2131296c7658Sdan ** instead. Or, if pbRetry is not NULL and a CHANGESET_DATA conflict occurs, 2132296c7658Sdan ** invoke the conflict handler. If it returns CHANGESET_REPLACE, set *pbRetry 2133296c7658Sdan ** to true before returning. In this case the caller will invoke this function 2134296c7658Sdan ** again, this time with pbRetry set to NULL. 2135296c7658Sdan ** 2136296c7658Sdan ** If argument pbReplace is NULL and a CHANGESET_CONFLICT conflict is 2137296c7658Sdan ** encountered invoke the conflict handler with CHANGESET_CONSTRAINT instead. 2138296c7658Sdan ** Or, if pbReplace is not NULL, invoke it with CHANGESET_CONFLICT. If such 2139296c7658Sdan ** an invocation returns SQLITE_CHANGESET_REPLACE, set *pbReplace to true 2140296c7658Sdan ** before retrying. In this case the caller attempts to remove the conflicting 2141296c7658Sdan ** row before invoking this function again, this time with pbReplace set 2142296c7658Sdan ** to NULL. 2143296c7658Sdan ** 2144296c7658Sdan ** If any conflict handler returns SQLITE_CHANGESET_ABORT, this function 2145296c7658Sdan ** returns SQLITE_ABORT. Otherwise, if no error occurs, SQLITE_OK is 2146296c7658Sdan ** returned. 2147296c7658Sdan */ 21480c698471Sdan static int sessionApplyOneOp( 2149296c7658Sdan sqlite3_changeset_iter *pIter, /* Changeset iterator */ 2150296c7658Sdan SessionApplyCtx *p, /* changeset_apply() context */ 21510c698471Sdan int(*xConflict)(void *, int, sqlite3_changeset_iter *), 2152296c7658Sdan void *pCtx, /* First argument for the conflict handler */ 2153296c7658Sdan int *pbReplace, /* OUT: True to remove PK row and retry */ 2154296c7658Sdan int *pbRetry /* OUT: True to retry. */ 21550c698471Sdan ){ 21560c698471Sdan const char *zDummy; 21570c698471Sdan int op; 21580c698471Sdan int nCol; 21590c698471Sdan int rc = SQLITE_OK; 21600c698471Sdan 21610c698471Sdan assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect ); 21620c698471Sdan assert( p->azCol && p->abPK ); 21630c698471Sdan assert( !pbReplace || *pbReplace==0 ); 21640c698471Sdan 21650c698471Sdan sqlite3changeset_op(pIter, &zDummy, &nCol, &op); 21660c698471Sdan 21670c698471Sdan if( op==SQLITE_DELETE ){ 21680c698471Sdan int i; 21690c698471Sdan 21700c698471Sdan /* Bind values to the DELETE statement. */ 21710c698471Sdan for(i=0; rc==SQLITE_OK && i<nCol; i++){ 21720c698471Sdan sqlite3_value *pVal; 21730c698471Sdan rc = sqlite3changeset_old(pIter, i, &pVal); 21740c698471Sdan if( rc==SQLITE_OK ){ 21750c698471Sdan rc = sqlite3_bind_value(p->pDelete, i+1, pVal); 21760c698471Sdan } 21770c698471Sdan } 2178*7cf7df7dSdan if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){ 2179*7cf7df7dSdan rc = sqlite3_bind_int(p->pDelete, nCol+1, pbRetry==0); 2180*7cf7df7dSdan } 21810c698471Sdan if( rc!=SQLITE_OK ) return rc; 21820c698471Sdan 21830c698471Sdan sqlite3_step(p->pDelete); 21840c698471Sdan rc = sqlite3_reset(p->pDelete); 21850c698471Sdan if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){ 21860c698471Sdan rc = sessionConflictHandler( 21870c698471Sdan SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry 21880c698471Sdan ); 21890c698471Sdan }else if( rc==SQLITE_CONSTRAINT ){ 21900c698471Sdan rc = sessionConflictHandler( 21910c698471Sdan SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0 21920c698471Sdan ); 21930c698471Sdan } 21940c698471Sdan 21950c698471Sdan }else if( op==SQLITE_UPDATE ){ 21960c698471Sdan int i; 21970c698471Sdan 21980c698471Sdan /* Bind values to the UPDATE statement. */ 21990c698471Sdan for(i=0; rc==SQLITE_OK && i<nCol; i++){ 22000c698471Sdan sqlite3_value *pOld = 0; 22010c698471Sdan sqlite3_value *pNew = 0; 22020c698471Sdan rc = sqlite3changeset_old(pIter, i, &pOld); 22030c698471Sdan if( rc==SQLITE_OK ){ 22040c698471Sdan rc = sqlite3changeset_new(pIter, i, &pNew); 22050c698471Sdan } 22060c698471Sdan if( rc==SQLITE_OK ){ 22070c698471Sdan if( pOld ) sqlite3_bind_value(p->pUpdate, i*3+1, pOld); 22080c698471Sdan sqlite3_bind_int(p->pUpdate, i*3+2, !!pNew); 22090c698471Sdan if( pNew ) sqlite3_bind_value(p->pUpdate, i*3+3, pNew); 22100c698471Sdan } 22110c698471Sdan } 22120c698471Sdan if( rc==SQLITE_OK ) rc = sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0); 22130c698471Sdan if( rc!=SQLITE_OK ) return rc; 22140c698471Sdan 22150c698471Sdan /* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict, 22160c698471Sdan ** the result will be SQLITE_OK with 0 rows modified. */ 22170c698471Sdan sqlite3_step(p->pUpdate); 22180c698471Sdan rc = sqlite3_reset(p->pUpdate); 22190c698471Sdan 22200c698471Sdan if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){ 22210c698471Sdan /* A NOTFOUND or DATA error. Search the table to see if it contains 22220c698471Sdan ** a row with a matching primary key. If so, this is a DATA conflict. 22230c698471Sdan ** Otherwise, if there is no primary key match, it is a NOTFOUND. */ 22240c698471Sdan 22250c698471Sdan rc = sessionConflictHandler( 22260c698471Sdan SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry 22270c698471Sdan ); 22280c698471Sdan 22290c698471Sdan }else if( rc==SQLITE_CONSTRAINT ){ 22300c698471Sdan /* This may be a CONSTRAINT or CONFLICT error. It is a CONFLICT if 22310c698471Sdan ** the only problem is a duplicate PRIMARY KEY, or a CONSTRAINT 22320c698471Sdan ** otherwise. */ 22330c698471Sdan int bPKChange = 0; 22340c698471Sdan 22350c698471Sdan /* Check if the PK has been modified. */ 22360c698471Sdan rc = SQLITE_OK; 22370c698471Sdan for(i=0; i<nCol && rc==SQLITE_OK; i++){ 22380c698471Sdan if( p->abPK[i] ){ 22390c698471Sdan sqlite3_value *pNew; 22400c698471Sdan rc = sqlite3changeset_new(pIter, i, &pNew); 22410c698471Sdan if( rc==SQLITE_OK && pNew ){ 22420c698471Sdan bPKChange = 1; 22430c698471Sdan break; 22440c698471Sdan } 22450c698471Sdan } 22460c698471Sdan } 22470c698471Sdan 22480c698471Sdan rc = sessionConflictHandler(SQLITE_CHANGESET_CONFLICT, 22490c698471Sdan p, pIter, xConflict, pCtx, (bPKChange ? pbReplace : 0) 22500c698471Sdan ); 22510c698471Sdan } 22520c698471Sdan 22530c698471Sdan }else{ 22540c698471Sdan int i; 22550c698471Sdan assert( op==SQLITE_INSERT ); 22560c698471Sdan for(i=0; rc==SQLITE_OK && i<nCol; i++){ 22570c698471Sdan sqlite3_value *pVal; 22580c698471Sdan rc = sqlite3changeset_new(pIter, i, &pVal); 22590c698471Sdan if( rc==SQLITE_OK ){ 22600c698471Sdan rc = sqlite3_bind_value(p->pInsert, i+1, pVal); 22610c698471Sdan } 22620c698471Sdan } 22630c698471Sdan if( rc!=SQLITE_OK ) return rc; 22640c698471Sdan 22650c698471Sdan sqlite3_step(p->pInsert); 22660c698471Sdan rc = sqlite3_reset(p->pInsert); 22670c698471Sdan if( rc==SQLITE_CONSTRAINT && xConflict ){ 22680c698471Sdan rc = sessionConflictHandler( 22690c698471Sdan SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, pbReplace 22700c698471Sdan ); 227137f133ecSdan } 227237f133ecSdan } 227337f133ecSdan 227437f133ecSdan return rc; 227537f133ecSdan } 227637f133ecSdan 2277296c7658Sdan /* 2278296c7658Sdan ** Apply the changeset passed via pChangeset/nChangeset to the main database 2279296c7658Sdan ** attached to handle "db". Invoke the supplied conflict handler callback 2280296c7658Sdan ** to resolve any conflicts encountered while applying the change. 2281296c7658Sdan */ 2282d5f0767cSdan int sqlite3changeset_apply( 2283296c7658Sdan sqlite3 *db, /* Apply change to "main" db of this handle */ 2284296c7658Sdan int nChangeset, /* Size of changeset in bytes */ 2285296c7658Sdan void *pChangeset, /* Changeset blob */ 2286d5f0767cSdan int(*xConflict)( 2287d5f0767cSdan void *pCtx, /* Copy of fifth arg to _apply() */ 2288d5f0767cSdan int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ 2289d5f0767cSdan sqlite3_changeset_iter *p /* Handle describing change and conflict */ 2290d5f0767cSdan ), 2291296c7658Sdan void *pCtx /* First argument passed to xConflict */ 2292d5f0767cSdan ){ 2293296c7658Sdan sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */ 2294296c7658Sdan int rc; /* Return code */ 2295d5f0767cSdan const char *zTab = 0; /* Name of current table */ 2296d5f0767cSdan int nTab = 0; /* Result of strlen(zTab) */ 2297296c7658Sdan SessionApplyCtx sApply; /* changeset_apply() context object */ 2298d5f0767cSdan 22990c698471Sdan memset(&sApply, 0, sizeof(sApply)); 2300d5f0767cSdan sqlite3changeset_start(&pIter, nChangeset, pChangeset); 23010c698471Sdan 23024c220252Sdan sqlite3_mutex_enter(sqlite3_db_mutex(db)); 23030c698471Sdan rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); 23040c698471Sdan while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter) ){ 23050c698471Sdan int nCol; 2306d5f0767cSdan int op; 23070c698471Sdan int bReplace = 0; 23080c698471Sdan int bRetry = 0; 23090c698471Sdan const char *zNew; 23100c698471Sdan sqlite3changeset_op(pIter, &zNew, &nCol, &op); 2311d5f0767cSdan 23120c698471Sdan if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){ 23130c698471Sdan sqlite3_free(sApply.azCol); 23140c698471Sdan sqlite3_finalize(sApply.pDelete); 23150c698471Sdan sqlite3_finalize(sApply.pUpdate); 23160c698471Sdan sqlite3_finalize(sApply.pInsert); 23170c698471Sdan sqlite3_finalize(sApply.pSelect); 23180c698471Sdan memset(&sApply, 0, sizeof(sApply)); 23190c698471Sdan sApply.db = db; 23200c698471Sdan sApply.nCol = nCol; 232137f133ecSdan 2322296c7658Sdan rc = sessionTableInfo( 2323296c7658Sdan db, "main", zNew, nCol, &zTab, &sApply.azCol, &sApply.abPK); 23240c698471Sdan 23250c698471Sdan if( rc!=SQLITE_OK 23260c698471Sdan || (rc = sessionSelectRow(db, zTab, &sApply)) 23270c698471Sdan || (rc = sessionUpdateRow(db, zTab, &sApply)) 23280c698471Sdan || (rc = sessionDeleteRow(db, zTab, &sApply)) 23290c698471Sdan || (rc = sessionInsertRow(db, zTab, &sApply)) 233037f133ecSdan ){ 233137f133ecSdan break; 233237f133ecSdan } 23330c698471Sdan 23340c698471Sdan nTab = strlen(zTab); 2335d5f0767cSdan } 2336d5f0767cSdan 23370c698471Sdan rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, &bReplace, &bRetry); 23380c698471Sdan 23390c698471Sdan if( rc==SQLITE_OK && bRetry ){ 23400c698471Sdan rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, &bReplace, 0); 23410c698471Sdan } 23420c698471Sdan 23430c698471Sdan if( bReplace ){ 23440c698471Sdan rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0); 23450c698471Sdan if( rc==SQLITE_OK ){ 2346d5f0767cSdan int i; 23470c698471Sdan for(i=0; i<sApply.nCol; i++){ 23480c698471Sdan if( sApply.abPK[i] ){ 2349d5f0767cSdan sqlite3_value *pVal; 2350d5f0767cSdan rc = sqlite3changeset_new(pIter, i, &pVal); 23510c698471Sdan if( rc==SQLITE_OK && pVal==0 ){ 23520c698471Sdan rc = sqlite3changeset_old(pIter, i, &pVal); 23530c698471Sdan } 2354d5f0767cSdan if( rc==SQLITE_OK ){ 23550c698471Sdan rc = sqlite3_bind_value(sApply.pDelete, i+1, pVal); 2356d5f0767cSdan } 2357d5f0767cSdan } 23580c698471Sdan } 23590c698471Sdan sqlite3_bind_int(sApply.pDelete, sApply.nCol+1, 1); 23600c698471Sdan } 23610c698471Sdan if( rc==SQLITE_OK ){ 23620c698471Sdan sqlite3_step(sApply.pDelete); 23630c698471Sdan rc = sqlite3_reset(sApply.pDelete); 23640c698471Sdan } 23650c698471Sdan if( rc==SQLITE_OK ){ 23660c698471Sdan rc = sessionApplyOneOp(pIter, &sApply, xConflict, pCtx, 0, 0); 23670c698471Sdan } 23680c698471Sdan if( rc==SQLITE_OK ){ 23690c698471Sdan rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0); 23700c698471Sdan } 23710c698471Sdan } 23720c698471Sdan } 2373d5f0767cSdan 2374296c7658Sdan if( rc==SQLITE_OK ){ 2375296c7658Sdan rc = sqlite3changeset_finalize(pIter); 2376296c7658Sdan }else{ 2377296c7658Sdan sqlite3changeset_finalize(pIter); 2378296c7658Sdan } 2379d5f0767cSdan 2380d5f0767cSdan if( rc==SQLITE_OK ){ 2381d5f0767cSdan rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0); 2382d5f0767cSdan }else{ 2383d5f0767cSdan sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0); 2384d5f0767cSdan sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0); 2385d5f0767cSdan } 2386d5f0767cSdan 23870c698471Sdan sqlite3_finalize(sApply.pInsert); 23880c698471Sdan sqlite3_finalize(sApply.pDelete); 23890c698471Sdan sqlite3_finalize(sApply.pUpdate); 23900c698471Sdan sqlite3_finalize(sApply.pSelect); 23910c698471Sdan sqlite3_free(sApply.azCol); 23924c220252Sdan sqlite3_mutex_leave(sqlite3_db_mutex(db)); 2393d5f0767cSdan return rc; 2394d5f0767cSdan } 239591ddd559Sdan 23964fccf43aSdan #endif /* #ifdef SQLITE_ENABLE_SESSION */ 2397