diff options
-rw-r--r-- | ext/session/sessionB.test | 233 | ||||
-rw-r--r-- | ext/session/sqlite3session.c | 213 | ||||
-rw-r--r-- | ext/session/sqlite3session.h | 9 | ||||
-rw-r--r-- | ext/session/test_session.c | 8 | ||||
-rw-r--r-- | manifest | 19 | ||||
-rw-r--r-- | manifest.uuid | 2 |
6 files changed, 435 insertions, 49 deletions
diff --git a/ext/session/sessionB.test b/ext/session/sessionB.test new file mode 100644 index 000000000..76213d8ab --- /dev/null +++ b/ext/session/sessionB.test @@ -0,0 +1,233 @@ +# 2014 August 16 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements regression tests for sessions SQLite extension. +# Specifically, this file contains tests for "patchset" changes. +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] session_common.tcl] +source $testdir/tester.tcl +ifcapable !session {finish_test; return} + +set testprefix sessionB + +# +# 1.*: Test that the blobs returned by the session_patchset() API are +# as expected. Also the sqlite3_changeset_iter functions. +# +# 2.*: Test that patchset blobs are handled by sqlite3changeset_apply(). +# +# 3.*: Test that sqlite3changeset_invert() works with patchset blobs. +# Correct behaviour is to return SQLITE_CORRUPT. + +proc do_patchset_test {tn session res} { + set r [list] + foreach x $res {lappend r $x} + uplevel do_test $tn [list [subst -nocommands { + set x [list] + sqlite3session_foreach c [$session patchset] { lappend x [set c] } + set x + }]] [list $r] +} + +proc do_sql2patchset_test {tn sql res} { + sqlite3session S db main + S attach * + execsql $sql + uplevel [list do_patchset_test $tn S $res] + S delete +} + +#------------------------------------------------------------------------- +# Run simple tests of the _patchset() API. +# +do_execsql_test 1.0 { + CREATE TABLE t1(a, b, c, d, PRIMARY KEY(d, a)); + INSERT INTO t1 VALUES(1, 2, 3, 4); + INSERT INTO t1 VALUES(5, 6, 7, 8); + INSERT INTO t1 VALUES(9, 10, 11, 12); +} + +do_test 1.1 { + sqlite3session S db main + S attach t1 + execsql { + INSERT INTO t1 VALUES('w', 'x', 'y', 'z'); + DELETE FROM t1 WHERE d=4; + UPDATE t1 SET c = 14 WHERE a=5; + } +} {} + +do_patchset_test 1.2 S { + {UPDATE t1 0 X..X {i 5 {} {} {} {} i 8} {{} {} {} {} i 14 {} {}}} + {INSERT t1 0 X..X {} {t w t x t y t z}} + {DELETE t1 0 X..X {i 1 {} {} {} {} i 4} {}} +} + +do_test 1.3 { + S delete +} {} + +do_sql2patchset_test 1.4 { + DELETE FROM t1; +} { + {DELETE t1 0 X..X {i 5 {} {} {} {} i 8} {}} + {DELETE t1 0 X..X {t w {} {} {} {} t z} {}} + {DELETE t1 0 X..X {i 9 {} {} {} {} i 12} {}} +} + +do_sql2patchset_test 1.5 { + INSERT INTO t1 VALUES(X'61626364', NULL, NULL, 4.2); + INSERT INTO t1 VALUES(4.2, NULL, NULL, X'61626364'); +} { + {INSERT t1 0 X..X {} {f 4.2 n {} n {} b abcd}} + {INSERT t1 0 X..X {} {b abcd n {} n {} f 4.2}} +} + +do_sql2patchset_test 1.6 { + UPDATE t1 SET b=45 WHERE typeof(a)=='blob'; + UPDATE t1 SET c='zzzz' WHERE typeof(a)!='blob'; +} { + {UPDATE t1 0 X..X {f 4.2 {} {} {} {} b abcd} {{} {} {} {} t zzzz {} {}}} + {UPDATE t1 0 X..X {b abcd {} {} {} {} f 4.2} {{} {} i 45 {} {} {} {}}} +} + +do_sql2patchset_test 1.7 { + UPDATE t1 SET b='xyz' WHERE typeof(a)=='blob'; + UPDATE t1 SET c='xyz' WHERE typeof(a)!='blob'; + UPDATE t1 SET b=45 WHERE typeof(a)=='blob'; + UPDATE t1 SET c='zzzz' WHERE typeof(a)!='blob'; +} { +} + +do_sql2patchset_test 1.8 { + DELETE FROM t1; +} { + {DELETE t1 0 X..X {f 4.2 {} {} {} {} b abcd} {}} + {DELETE t1 0 X..X {b abcd {} {} {} {} f 4.2} {}} +} + +#------------------------------------------------------------------------- +# Run simple tests of _apply() with patchset objects. +# +reset_db + +proc noop {args} { error $args } +proc exec_rollback_replay {sql} { + sqlite3session S db main + S attach * + execsql BEGIN + execsql $sql + set patchset [S patchset] + S delete + execsql ROLLBACK + sqlite3changeset_apply db $patchset noop +} + +do_execsql_test 2.0 { + CREATE TABLE t2(a, b, c, d, PRIMARY KEY(b,c)); + CREATE TABLE t3(w, x, y, z, PRIMARY KEY(w)); +} + +do_test 2.1 { + exec_rollback_replay { + INSERT INTO t2 VALUES(1, 2, 3, 4); + INSERT INTO t2 VALUES('w', 'x', 'y', 'z'); + } + execsql { SELECT * FROM t2 } +} {1 2 3 4 w x y z} + +do_test 2.2 { + exec_rollback_replay { + DELETE FROM t2 WHERE a=1; + UPDATE t2 SET d = 'a'; + } + execsql { SELECT * FROM t2 } +} {w x y a} + +#------------------------------------------------------------------------- +# sqlite3changeset_invert() +# +reset_db + +do_execsql_test 3.1 { CREATE TABLE t1(x PRIMARY KEY, y) } +do_test 3.2 { + sqlite3session S db main + S attach * + execsql { INSERT INTO t1 VALUES(1, 2) } + set patchset [S patchset] + S delete + list [catch { sqlite3changeset_invert $patchset } msg] [set msg] +} {1 SQLITE_CORRUPT} + + +#------------------------------------------------------------------------- +# sqlite3changeset_concat() +# +reset_db + +proc do_patchconcat_test {tn args} { + set nSql [expr [llength $args]-1] + set res [lindex $args $nSql] + set patchlist [list] + + execsql BEGIN + foreach sql [lrange $args 0 end-1] { + sqlite3session S db main + S attach * + execsql $sql + lappend patchlist [S patchset] + S delete + } + execsql ROLLBACK + + set patch [lindex $patchlist 0] + foreach p [lrange $patchlist 1 end] { + set patch [sqlite3changeset_concat $patch $p] + } + + set x [list] + sqlite3session_foreach c $patch { lappend x $c } + + uplevel [list do_test $tn [list set {} $x] [list {*}$res]] +} + +do_execsql_test 4.1.1 { + CREATE TABLE t1(x PRIMARY KEY, y, z); +} +do_patchconcat_test 4.1.2 { + INSERT INTO t1 VALUES(1, 2, 3); +} { + INSERT INTO t1 VALUES(4, 5, 6); +} { + {INSERT t1 0 X.. {} {i 1 i 2 i 3}} + {INSERT t1 0 X.. {} {i 4 i 5 i 6}} +} + +if 0 { +do_execsql_test 4.2.1 { + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t1 VALUES(4, 5, 6); +} +do_patchconcat_test 4.2.2 { + UPDATE t1 SET z = 'abc' WHERE x=1 +} { + UPDATE t1 SET z = 'def' WHERE x=4 +} { +} +} + +finish_test + + diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index 256d5f089..62adb8de2 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -35,6 +35,7 @@ struct sqlite3_session { struct sqlite3_changeset_iter { u8 *aChangeset; /* Pointer to buffer containing changeset */ int nChangeset; /* Number of bytes in aChangeset */ + int bPatchset; /* True if this is a patchset */ u8 *pNext; /* Pointer to next change within aChangeset */ int rc; /* Iterator error code */ sqlite3_stmt *pConflict; /* Points to conflicting row, if any */ @@ -122,6 +123,7 @@ struct SessionTable { ** ** 1 byte: Constant 0x54 (capital 'T') ** Varint: Big-endian integer set to the number of columns in the table. +** nCol bytes: 0x01 for PK columns, 0x00 otherwise. ** N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated. ** ** Followed by one or more changes to the table. @@ -130,6 +132,25 @@ struct SessionTable { ** 1 byte: The "indirect-change" flag. ** old.* record: (delete and update only) ** new.* record: (insert and update only) +** +** PATCHSET FORMAT: +** +** A patchset is also a collection of changes. It is similar to a changeset, +** but omits those fields that are not useful if no conflict resolution +** is required when applying the changeset. +** +** Each group of changes begins with a table header: +** +** 1 byte: Constant 0x50 (capital 'P') +** Varint: Big-endian integer set to the number of columns in the table. +** nCol bytes: 0x01 for PK columns, 0x00 otherwise. +** N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated. +** +** Followed by one or more changes to the table. +** +** 1 byte: Either SQLITE_INSERT, UPDATE or DELETE. +** 1 byte: The "indirect-change" flag. +** single record: (PK fields for DELETE, or full record for INSERT/UPDATE). */ /* @@ -1449,6 +1470,7 @@ static void sessionAppendCol( */ static int sessionAppendUpdate( SessionBuffer *pBuf, /* Buffer to append to */ + int bPatchset, /* True for "patchset", 0 for "changeset" */ sqlite3_stmt *pStmt, /* Statement handle pointing at new row */ SessionChange *p, /* Object containing old values */ u8 *abPK /* Boolean array - true for PK columns */ @@ -1506,15 +1528,23 @@ static int sessionAppendUpdate( } } - if( bChanged || abPK[i] ){ - sessionAppendBlob(pBuf, pCsr, nAdvance, &rc); - }else{ - sessionAppendByte(pBuf, 0, &rc); + /* If at least one field has been modified, this is not a no-op. */ + if( bChanged ) bNoop = 0; + + /* Add a field to the old.* record. This is omitted if this modules is + ** currently generating a patchset. */ + if( bPatchset==0 ){ + if( bChanged || abPK[i] ){ + sessionAppendBlob(pBuf, pCsr, nAdvance, &rc); + }else{ + sessionAppendByte(pBuf, 0, &rc); + } } - if( bChanged ){ + /* Add a field to the new.* record. Or the only record if currently + ** generating a patchset. */ + if( bChanged || (bPatchset && abPK[i]) ){ sessionAppendCol(&buf2, pStmt, i, &rc); - bNoop = 0; }else{ sessionAppendByte(&buf2, 0, &rc); } @@ -1532,6 +1562,56 @@ static int sessionAppendUpdate( return rc; } +static int sessionAppendDelete( + SessionBuffer *pBuf, /* Buffer to append to */ + int bPatchset, /* True for "patchset", 0 for "changeset" */ + sqlite3_stmt *pStmt, /* Statement handle pointing at new row */ + SessionChange *p, /* Object containing old values */ + u8 *abPK /* Boolean array - true for PK columns */ +){ + int rc = SQLITE_OK; + + sessionAppendByte(pBuf, SQLITE_DELETE, &rc); + sessionAppendByte(pBuf, p->bIndirect, &rc); + + if( bPatchset==0 ){ + sessionAppendBlob(pBuf, p->aRecord, p->nRecord, &rc); + }else{ + int nCol = sqlite3_column_count(pStmt); + int i; + u8 *a = p->aRecord; + for(i=0; i<nCol; i++){ + u8 *pStart = a; + int eType = *a++; + + switch( eType ){ + case 0: + case SQLITE_NULL: + assert( abPK[i]==0 ); + break; + + case SQLITE_FLOAT: + case SQLITE_INTEGER: + a += 8; + break; + + default: { + int n; + a += sessionVarintGet(a, &n); + a += n; + break; + } + } + if( abPK[i] ){ + sessionAppendBlob(pBuf, pStart, a-pStart, &rc); + } + } + assert( (a - p->aRecord)==p->nRecord ); + } + + return rc; +} + /* ** Formulate and prepare a SELECT statement to retrieve a row from table ** zTab in database zDb based on its primary key. i.e. @@ -1654,25 +1734,20 @@ static int sessionSelectBind( */ static void sessionAppendTableHdr( SessionBuffer *pBuf, + int bPatchset, SessionTable *pTab, int *pRc ){ /* Write a table header */ - sessionAppendByte(pBuf, 'T', pRc); + sessionAppendByte(pBuf, (bPatchset ? 'P' : 'T'), pRc); sessionAppendVarint(pBuf, pTab->nCol, pRc); sessionAppendBlob(pBuf, pTab->abPK, pTab->nCol, pRc); sessionAppendBlob(pBuf, (u8 *)pTab->zName, (int)strlen(pTab->zName)+1, pRc); } -/* -** Obtain a changeset object containing all changes recorded by the -** session object passed as the first argument. -** -** It is the responsibility of the caller to eventually free the buffer -** using sqlite3_free(). -*/ -int sqlite3session_changeset( +int sessionGenerateChangeset( sqlite3_session *pSession, /* Session object */ + int bPatchset, /* True for patchset, false for changeset */ int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ void **ppChangeset /* OUT: Buffer containing changeset */ ){ @@ -1711,7 +1786,7 @@ int sqlite3session_changeset( } /* Write a table header */ - sessionAppendTableHdr(&buf, pTab, &rc); + sessionAppendTableHdr(&buf, bPatchset, pTab, &rc); /* Build and compile a statement to execute: */ if( rc==SQLITE_OK ){ @@ -1735,13 +1810,10 @@ int sqlite3session_changeset( sessionAppendCol(&buf, pSel, iCol, &rc); } }else{ - rc = sessionAppendUpdate(&buf, pSel, p, abPK); + rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK); } }else if( p->op!=SQLITE_INSERT ){ - /* A DELETE change */ - sessionAppendByte(&buf, SQLITE_DELETE, &rc); - sessionAppendByte(&buf, p->bIndirect, &rc); - sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc); + rc = sessionAppendDelete(&buf, bPatchset, pSel, p, abPK); } if( rc==SQLITE_OK ){ rc = sqlite3_reset(pSel); @@ -1770,6 +1842,36 @@ int sqlite3session_changeset( } /* +** Obtain a changeset object containing all changes recorded by the +** session object passed as the first argument. +** +** It is the responsibility of the caller to eventually free the buffer +** using sqlite3_free(). +*/ +int sqlite3session_changeset( + sqlite3_session *pSession, /* Session object */ + int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ + void **ppChangeset /* OUT: Buffer containing changeset */ +){ + return sessionGenerateChangeset(pSession, 0, pnChangeset, ppChangeset); +} + +/* +** Obtain a patchset object containing all changes recorded by the +** session object passed as the first argument. +** +** It is the responsibility of the caller to eventually free the buffer +** using sqlite3_free(). +*/ +int sqlite3session_patchset( + sqlite3_session *pSession, /* Session object */ + int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */ + void **ppPatchset /* OUT: Buffer containing changeset */ +){ + return sessionGenerateChangeset(pSession, 1, pnPatchset, ppPatchset); +} + +/* ** Enable or disable the session object passed as the first argument. */ int sqlite3session_enable(sqlite3_session *pSession, int bEnable){ @@ -1866,13 +1968,16 @@ int sqlite3changeset_start( static int sessionReadRecord( u8 **paChange, /* IN/OUT: Pointer to binary record */ int nCol, /* Number of values in record */ + u8 *abPK, /* Array of primary key flags, or NULL */ sqlite3_value **apOut /* Write values to this array */ ){ int i; /* Used to iterate through columns */ u8 *aRec = *paChange; /* Cursor for the serialized record */ for(i=0; i<nCol; i++){ - int eType = *aRec++; /* Type of value (SQLITE_NULL, TEXT etc.) */ + int eType; + if( abPK && abPK[i]==0 ) continue; + eType = *aRec++; /* Type of value (SQLITE_NULL, TEXT etc.) */ assert( !apOut || apOut[i]==0 ); if( eType ){ if( apOut ){ @@ -1952,8 +2057,9 @@ static int sessionChangesetNext( } aChange = p->pNext; - if( aChange[0]=='T' ){ + if( aChange[0]=='T' || aChange[0]=='P' ){ int nByte; /* Bytes to allocate for apValue */ + p->bPatchset = (aChange[0]=='P'); aChange++; aChange += sessionVarintGet(aChange, &p->nCol); p->abPK = (u8 *)aChange; @@ -1981,18 +2087,36 @@ static int sessionChangesetNext( if( paRec ){ *paRec = aChange; } /* If this is an UPDATE or DELETE, read the old.* record. */ - if( p->op!=SQLITE_INSERT ){ - p->rc = sessionReadRecord(&aChange, p->nCol, paRec?0:p->apValue); + if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){ + u8 *abPK = p->bPatchset ? p->abPK : 0; + p->rc = sessionReadRecord(&aChange, p->nCol, abPK, paRec?0:p->apValue); if( p->rc!=SQLITE_OK ) return p->rc; } /* If this is an INSERT or UPDATE, read the new.* record. */ if( p->op!=SQLITE_DELETE ){ - p->rc = sessionReadRecord(&aChange, p->nCol, paRec?0:&p->apValue[p->nCol]); + sqlite3_value **apOut = (paRec ? 0 : &p->apValue[p->nCol]); + p->rc = sessionReadRecord(&aChange, p->nCol, 0, apOut); if( p->rc!=SQLITE_OK ) return p->rc; } - if( pnRec ){ *pnRec = (int)(aChange - *paRec); } + if( pnRec ){ + *pnRec = (int)(aChange - *paRec); + }else if( p->bPatchset && p->op==SQLITE_UPDATE ){ + /* If this is an UPDATE that is part of a patchset, then all PK and + ** modified fields are present in the new.* record. The old.* record + ** is currently completely empty. This block shifts the PK fields from + ** new.* to old.*, to accommodate the code that reads these arrays. */ + int i; + for(i=0; i<p->nCol; i++){ + assert( p->apValue[i]==0 ); + assert( p->abPK[i]==0 || p->apValue[i+p->nCol] ); + if( p->abPK[i] ){ + p->apValue[i] = p->apValue[i+p->nCol]; + p->apValue[i+p->nCol] = 0; + } + } + } p->pNext = aChange; return SQLITE_ROW; } @@ -2225,7 +2349,7 @@ int sqlite3changeset_invert( int nByte; u8 *aEnd = &aIn[i+2]; - sessionReadRecord(&aEnd, nCol, 0); + sessionReadRecord(&aEnd, nCol, 0, 0); aOut[i] = (eType==SQLITE_DELETE ? SQLITE_INSERT : SQLITE_DELETE); aOut[i+1] = aIn[i+1]; nByte = (int)(aEnd - &aIn[i+2]); @@ -2249,9 +2373,9 @@ int sqlite3changeset_invert( } /* Read the old.* and new.* records for the update change. */ - rc = sessionReadRecord(&aEnd, nCol, &apVal[0]); + rc = sessionReadRecord(&aEnd, nCol, 0, &apVal[0]); if( rc==SQLITE_OK ){ - rc = sessionReadRecord(&aEnd, nCol, &apVal[nCol]); + rc = sessionReadRecord(&aEnd, nCol, 0, &apVal[nCol]); } /* Write the header for the new UPDATE change. Same as the original. */ @@ -2781,10 +2905,21 @@ static int sessionApplyOneOp( if( op==SQLITE_DELETE ){ - /* Bind values to the DELETE statement. */ - rc = sessionBindRow(pIter, sqlite3changeset_old, nCol, 0, p->pDelete); + /* Bind values to the DELETE statement. If conflict handling is required, + ** bind values for all columns and set bound variable (nCol+1) to true. + ** Or, if conflict handling is not required, bind just the PK column + ** values and, if it exists, set (nCol+1) to false. Conflict handling + ** is not required if: + ** + ** * this is a patchset, or + ** * (pbRetry==0), or + ** * all columns of the table are PK columns (in this case there is + ** no (nCol+1) variable to bind to). + */ + u8 *abPK = (pIter->bPatchset ? p->abPK : 0); + rc = sessionBindRow(pIter, sqlite3changeset_old, nCol, abPK, p->pDelete); if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){ - rc = sqlite3_bind_int(p->pDelete, nCol+1, pbRetry==0); + rc = sqlite3_bind_int(p->pDelete, nCol+1, (pbRetry==0 || abPK)); } if( rc!=SQLITE_OK ) return rc; @@ -2816,7 +2951,9 @@ static int sessionApplyOneOp( rc = sessionBindValue(p->pUpdate, i*3+3, pNew); } } - if( rc==SQLITE_OK ) sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0 || pIter->bPatchset); + } if( rc!=SQLITE_OK ) return rc; /* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict, @@ -3099,7 +3236,7 @@ static int sessionChangeMerge( u8 *a1 = aRec; assert( op2==SQLITE_UPDATE ); pNew->op = SQLITE_INSERT; - sessionReadRecord(&a1, pTab->nCol, 0); + sessionReadRecord(&a1, pTab->nCol, 0, 0); sessionMergeRecord(&aCsr, pTab->nCol, pExist->aRecord, a1); }else if( op1==SQLITE_DELETE ){ /* DELETE + INSERT */ assert( op2==SQLITE_INSERT ); @@ -3112,8 +3249,8 @@ static int sessionChangeMerge( u8 *a1 = pExist->aRecord; u8 *a2 = aRec; assert( op1==SQLITE_UPDATE ); - sessionReadRecord(&a1, pTab->nCol, 0); - sessionReadRecord(&a2, pTab->nCol, 0); + sessionReadRecord(&a1, pTab->nCol, 0, 0); + sessionReadRecord(&a2, pTab->nCol, 0, 0); pNew->op = SQLITE_UPDATE; if( 0==sessionMergeUpdate(&aCsr, pTab, aRec, pExist->aRecord, a1, a2) ){ sqlite3_free(pNew); @@ -3274,7 +3411,7 @@ int sqlite3changeset_concat( int i; if( pTab->nEntry==0 ) continue; - sessionAppendTableHdr(&buf, pTab, &rc); + sessionAppendTableHdr(&buf, 0, pTab, &rc); for(i=0; i<pTab->nChange; i++){ SessionChange *p; for(p=pTab->apChange[i]; p; p=p->pNext){ diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index 974a770c7..de4ee77b9 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -274,6 +274,15 @@ int sqlite3session_changeset( ); /* +** CAPI3REF: Generate A Patchset From A Session Object +*/ +int sqlite3session_patchset( + sqlite3_session *pSession, /* Session object */ + int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */ + void **ppPatchset /* OUT: Buffer containing changeset */ +); + +/* ** CAPI3REF: Test if a changeset has recorded any changes. ** ** Return non-zero if no changes to attached tables have been recorded by diff --git a/ext/session/test_session.c b/ext/session/test_session.c index f1c2fbe9a..fa99f5678 100644 --- a/ext/session/test_session.c +++ b/ext/session/test_session.c @@ -73,6 +73,7 @@ static int test_session_cmd( { "indirect", 1, "BOOL", }, /* 4 */ { "isempty", 0, "", }, /* 5 */ { "table_filter", 1, "SCRIPT", }, /* 6 */ + { "patchset", 0, "", }, /* 7 */ { 0 } }; int iSub; @@ -102,10 +103,15 @@ static int test_session_cmd( break; } + case 7: /* patchset */ case 1: { /* changeset */ int nChange; void *pChange; - rc = sqlite3session_changeset(pSession, &nChange, &pChange); + if( iSub==7 ){ + rc = sqlite3session_patchset(pSession, &nChange, &pChange); + }else{ + rc = sqlite3session_changeset(pSession, &nChange, &pChange); + } if( rc==SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pChange, nChange)); sqlite3_free(pChange); @@ -1,5 +1,5 @@ -C Update\sthe\ssessions\sbranch\sfor\sversion\s3.8.6. -D 2014-08-15T15:10:46.167 +C Begin\sadding\sthe\ssqlite3session_patchset()\sAPI\sto\sthe\ssessions\sextension.\sThis\sis\san\sinterim\scommit. +D 2014-08-15T20:15:49.367 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 639859a6f81bd15921ccd56ddbd6dfd335278377 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -152,11 +152,12 @@ F ext/session/session6.test 443789bc2fca12e4f7075cf692c60b8a2bea1a26 F ext/session/session8.test 7d35947ad329b8966f095d34f9617a9eff52dc65 F ext/session/session9.test 776e46785c29c11cda01f5205d0f1e8f8f9a46bf F ext/session/sessionA.test eb05c13e4ef1ca8046a3a6dbf2d5f6f5b04a11d4 +F ext/session/sessionB.test dbabf40e7580f2dc245ea6b37b94fd1ec0b25fc3 F ext/session/session_common.tcl 1539d8973b2aea0025c133eb0cc4c89fcef541a5 F ext/session/sessionfault.test 496291b287ba3c0b14ca2e074425e29cc92a64a6 -F ext/session/sqlite3session.c 34e19186d05d534e5a37a4f5a8a3c3e24e3fa88a -F ext/session/sqlite3session.h 6c35057241567ed6319f750ee504a81c459225e1 -F ext/session/test_session.c 7878ac0e2fe9675e8ec24d54f6a538ccc105977f +F ext/session/sqlite3session.c e0d8101afc5df85495c401eadf2cb288c3d53adc +F ext/session/sqlite3session.h c99445ea9918343d3e62acafdf82bc5502cfba29 +F ext/session/test_session.c 920ccb6d6e1df263cd9099563328094c230b2925 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 @@ -1201,7 +1202,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P d49455d9a972fc2224d9beb97165a998ca56e838 9491ba7d738528f168657adb43a198238abde19e -R f94212c9f19d7229cfd6e84a8137e8b7 -U drh -Z 80e27e19ffdd26307df7b50bec8606de +P 2acbeac1fd9b9feb26e1c24d4ae50ce79f17a3f8 +R 3c0b82c93c7620c8b999c9243f48a749 +U dan +Z a8da13936a56069d46e167f9c70b2c2a diff --git a/manifest.uuid b/manifest.uuid index 79ed17684..ccaf220a3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2acbeac1fd9b9feb26e1c24d4ae50ce79f17a3f8
\ No newline at end of file +60a4565a8c44762a002cd02979317df5ca47e899
\ No newline at end of file |