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