From 3739f29807180878a77c37405f3f166c05dfb086 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 17 Jan 2018 20:57:20 +0000 Subject: [PATCH 1/5] Fix a problem in the sessions module with logging sqlite_stat1 rows for which (idx IS NULL) is true. FossilOrigin-Name: 25bf734be1b3883fccf12ac4d93d50289aa307fb60a52e0e32df12f7ee4edc7a --- ext/session/sessionstat1.test | 109 +++++++++++++++++++++ ext/session/sqlite3session.c | 176 +++++++++++++++++++++++++--------- manifest | 19 ++-- manifest.uuid | 2 +- src/analyze.c | 3 + 5 files changed, 253 insertions(+), 56 deletions(-) diff --git a/ext/session/sessionstat1.test b/ext/session/sessionstat1.test index 59de1cf1b4..22b2de8dbc 100644 --- a/ext/session/sessionstat1.test +++ b/ext/session/sessionstat1.test @@ -121,5 +121,114 @@ do_execsql_test -db db2 2.4 { do_execsql_test -db db2 2.5 { SELECT count(*) FROM t1 } 32 +#------------------------------------------------------------------------- +db2 close +forcedelete test.db2 +reset_db +sqlite3 db2 test.db2 + +do_test 3.0 { + do_common_sql { + CREATE TABLE t1(a, b, c); + ANALYZE; + DELETE FROM sqlite_stat1; + } + execsql { + INSERT INTO t1 VALUES(1, 1, 1); + INSERT INTO t1 VALUES(2, 2, 2); + INSERT INTO t1 VALUES(3, 3, 3); + INSERT INTO t1 VALUES(4, 4, 4); + } +} {} + +do_iterator_test 3.1 {} { + ANALYZE +} { + {INSERT sqlite_stat1 0 XX. {} {t t1 b {} t 4}} +} +db null null +db2 null null +do_execsql_test 3.2 { + SELECT * FROM sqlite_stat1; +} {t1 null 4} +do_test 3.3 { + execsql { DELETE FROM sqlite_stat1 } + do_then_apply_sql { ANALYZE } + execsql { SELECT * FROM sqlite_stat1 } db2 +} {t1 null 4} +do_test 3.4 { + execsql { INSERT INTO t1 VALUES(5,5,5) } + do_then_apply_sql { ANALYZE } + execsql { SELECT * FROM sqlite_stat1 } db2 +} {t1 null 5} +do_test 3.5 { + do_then_apply_sql { DROP TABLE t1 } + execsql { SELECT * FROM sqlite_stat1 } db2 +} {} + +do_test 3.6.1 { + execsql { + CREATE TABLE t1(a, b, c); + CREATE TABLE t2(x, y, z); + INSERT INTO t1 VALUES(1,1,1), (2,2,2), (3,3,3), (4,4,4), (5,5,5); + INSERT INTO t2 SELECT * FROM t1; + DELETE FROM sqlite_stat1; + } + sqlite3session S db main + S attach sqlite_stat1 + execsql { ANALYZE } +} {} +do_changeset_test 3.6.2 S { + {INSERT sqlite_stat1 0 XX. {} {t t2 b {} t 5}} + {INSERT sqlite_stat1 0 XX. {} {t t1 b {} t 5}} +} +do_changeset_invert_test 3.6.3 S { + {DELETE sqlite_stat1 0 XX. {t t2 b {} t 5} {}} + {DELETE sqlite_stat1 0 XX. {t t1 b {} t 5} {}} +} +do_test 3.6.4 { S delete } {} + +proc sql_changeset_concat {args} { + foreach sql $args { + sqlite3session S db main + S attach sqlite_stat1 + execsql $sql + set change [S changeset] + S delete + + if {[info vars ret]!=""} { + set ret [sqlite3changeset_concat $ret $change] + } else { + set ret $change + } + } + + changeset_to_list $ret +} + +proc do_scc_test {tn args} { + uplevel [list \ + do_test $tn [concat sql_changeset_concat [lrange $args 0 end-1]] \ + [list {*}[ lindex $args end ]] + ] +} + +do_execsql_test 3.7.0 { + DELETE FROM sqlite_stat1; +} +do_scc_test 3.7.1 { + ANALYZE; +} { + INSERT INTO t2 VALUES(6,6,6); + ANALYZE; +} { + {INSERT sqlite_stat1 0 XX. {} {t t1 b {} t 5}} + {INSERT sqlite_stat1 0 XX. {} {t t2 b {} t 6}} +} + + + + + finish_test diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index 6ef90037de..cbe75d5667 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -113,6 +113,7 @@ struct SessionTable { SessionTable *pNext; char *zName; /* Local name of table */ int nCol; /* Number of columns in table zName */ + int bStat1; /* True if this is sqlite_stat1 */ const char **azCol; /* Column names */ u8 *abPK; /* Array of primary key flags */ int nEntry; /* Total number of entries in hash table */ @@ -472,31 +473,35 @@ static int sessionPreupdateHash( if( rc!=SQLITE_OK ) return rc; eType = sqlite3_value_type(pVal); - h = sessionHashAppendType(h, eType); - if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ - i64 iVal; - if( eType==SQLITE_INTEGER ){ - iVal = sqlite3_value_int64(pVal); - }else{ - double rVal = sqlite3_value_double(pVal); - assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); - memcpy(&iVal, &rVal, 8); - } - h = sessionHashAppendI64(h, iVal); - }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ - const u8 *z; - int n; - if( eType==SQLITE_TEXT ){ - z = (const u8 *)sqlite3_value_text(pVal); - }else{ - z = (const u8 *)sqlite3_value_blob(pVal); - } - n = sqlite3_value_bytes(pVal); - if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; - h = sessionHashAppendBlob(h, n, z); + if( pTab->bStat1 && eType==SQLITE_NULL ){ + h = sessionHashAppendType(h, SQLITE_BLOB); }else{ - assert( eType==SQLITE_NULL ); - *pbNullPK = 1; + h = sessionHashAppendType(h, eType); + if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ + i64 iVal; + if( eType==SQLITE_INTEGER ){ + iVal = sqlite3_value_int64(pVal); + }else{ + double rVal = sqlite3_value_double(pVal); + assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); + memcpy(&iVal, &rVal, 8); + } + h = sessionHashAppendI64(h, iVal); + }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ + const u8 *z; + int n; + if( eType==SQLITE_TEXT ){ + z = (const u8 *)sqlite3_value_text(pVal); + }else{ + z = (const u8 *)sqlite3_value_blob(pVal); + } + n = sqlite3_value_bytes(pVal); + if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; + h = sessionHashAppendBlob(h, n, z); + }else{ + assert( eType==SQLITE_NULL ); + *pbNullPK = 1; + } } } } @@ -550,7 +555,7 @@ static unsigned int sessionChangeHash( || eType==SQLITE_TEXT || eType==SQLITE_BLOB || eType==SQLITE_NULL || eType==0 ); - assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) ); + assert( !isPK || (eType!=0 && (pTab->bStat1 || eType!=SQLITE_NULL)) ); if( isPK ){ a++; @@ -558,7 +563,7 @@ static unsigned int sessionChangeHash( if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ h = sessionHashAppendI64(h, sessionGetI64(a)); a += 8; - }else{ + }else if( eType!=SQLITE_NULL ){ int n; a += sessionVarintGet(a, &n); h = sessionHashAppendBlob(h, n, a); @@ -794,6 +799,7 @@ static int sessionPreupdateEqual( sqlite3_value *pVal; /* Value returned by preupdate_new/old */ int rc; /* Error code from preupdate_new/old */ int eType = *a++; /* Type of value from change record */ + int eValType; /* The following calls to preupdate_new() and preupdate_old() can not ** fail. This is because they cache their return values, and by the @@ -808,7 +814,14 @@ static int sessionPreupdateEqual( rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal); } assert( rc==SQLITE_OK ); - if( sqlite3_value_type(pVal)!=eType ) return 0; + eValType = sqlite3_value_type(pVal); + if( eType==SQLITE_BLOB && eValType==SQLITE_NULL && pTab->bStat1 ){ + int n; + a += sessionVarintGet(a, &n); + if( n!=0 ) return 0; + continue; + } + if( eValType!=eType ) return 0; /* A SessionChange object never has a NULL value in a PK column */ assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT @@ -1047,6 +1060,9 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ break; } } + if( 0==sqlite3_stricmp("sqlite_stat1", pTab->zName) ){ + pTab->bStat1 = 1; + } } } return (pSession->rc || pTab->abPK==0); @@ -1093,7 +1109,7 @@ static void sessionPreupdateOneChange( rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull); if( rc!=SQLITE_OK ) goto error_out; - if( bNull==0 ){ + if( bNull==0 || pTab->bStat1 ){ /* Search the hash table for an existing record for this row. */ SessionChange *pC; for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){ @@ -1128,6 +1144,7 @@ static void sessionPreupdateOneChange( rc = sessionSerializeValue(0, p, &nByte); if( rc!=SQLITE_OK ) goto error_out; } + if( pTab->bStat1 ) nByte += 30; /* Allocate the change object */ pChange = (SessionChange *)sqlite3_malloc(nByte); @@ -1151,7 +1168,12 @@ static void sessionPreupdateOneChange( }else if( pTab->abPK[i] ){ pSession->hook.xNew(pSession->hook.pCtx, i, &p); } - sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte); + if( p && pTab->bStat1 && sqlite3_value_type(p)==SQLITE_NULL ){ + pChange->aRecord[nByte++] = SQLITE_BLOB; + nByte += sessionVarintPut(&pChange->aRecord[nByte], 0); + }else{ + sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte); + } } /* Add the change to the hash-table */ @@ -2104,28 +2126,41 @@ static int sessionSelectStmt( sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */ ){ int rc = SQLITE_OK; - int i; - const char *zSep = ""; - SessionBuffer buf = {0, 0, 0}; + char *zSql = 0; + int nSql = -1; - sessionAppendStr(&buf, "SELECT * FROM ", &rc); - sessionAppendIdent(&buf, zDb, &rc); - sessionAppendStr(&buf, ".", &rc); - sessionAppendIdent(&buf, zTab, &rc); - sessionAppendStr(&buf, " WHERE ", &rc); - for(i=0; ipInsert, + "INSERT INTO main.sqlite_stat1 VALUES(?1, " + "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END, " + "?3)" + ); + } + if( rc==SQLITE_OK ){ + rc = sessionPrepare(db, &p->pUpdate, + "UPDATE main.sqlite_stat1 SET " + "tbl = CASE WHEN ?2 THEN ?3 ELSE tbl END, " + "idx = CASE WHEN ?5 THEN ?6 ELSE idx END, " + "stat = CASE WHEN ?8 THEN ?9 ELSE stat END " + "WHERE tbl=?1 AND idx IS " + "CASE WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL ELSE ?4 END " + "AND (?10 OR ?8=0 OR stat IS ?7)" + ); + } + if( rc==SQLITE_OK ){ + rc = sessionPrepare(db, &p->pDelete, + "DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS " + "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END " + "AND (?4 OR stat IS ?3)" + ); + } + assert( rc==SQLITE_OK ); + return rc; +} + /* ** A wrapper around sqlite3_bind_value() that detects an extra problem. ** See comments in the body of this function for details. @@ -4092,6 +4169,11 @@ static int sessionChangesetApply( } else{ sApply.nCol = nCol; + if( 0==sqlite3_stricmp(zTab, "sqlite_stat1") ){ + if( (rc = sessionStat1Sql(db, &sApply) ) ){ + break; + } + }else if((rc = sessionSelectRow(db, zTab, &sApply)) || (rc = sessionUpdateRow(db, zTab, &sApply)) || (rc = sessionDeleteRow(db, zTab, &sApply)) diff --git a/manifest b/manifest index 5e365cf00f..ce17059a62 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\scausing\sthe\ssessions\smodule\sto\soccasionally\slose\strack\sof\srows\nwith\scomposite\sprimary\skeys\swhen\sthere\sare\stwo\srows\swith\sthe\ssame\stext\svalue\nin\sthe\sleftmost\scolumn\sof\sthe\sPK. -D 2018-01-17T17:38:18.448 +C Fix\sa\sproblem\sin\sthe\ssessions\smodule\swith\slogging\ssqlite_stat1\srows\sfor\swhich\n(idx\sIS\sNULL)\sis\strue. +D 2018-01-17T20:57:20.602 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 38f84f301cbef443b2d269f67a74b8cc536469831f70df7c3e912acc04932cc2 @@ -398,9 +398,9 @@ F ext/session/sessionat.test efe88965e74ff1bc2af9c310b28358c02d420c1fb2705cc7a28 F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7 F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0 -F ext/session/sessionstat1.test e3a3f5876ce1526b48f6f447ee0b18960ac683e3fc891791e1ca0c08e823d498 +F ext/session/sessionstat1.test 16268a9bf62ab19c9bc9e41404bf7a13b5fd37c9cb6cf278a472f0c6c50d7ac1 F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc -F ext/session/sqlite3session.c c9d813ffa8db0670257da0a0c931e7188dfbb0010bf1a38274775c9b6300dd5c +F ext/session/sqlite3session.c 870d142ec13c0e053139e7f3427aa550e59faf1775ae2bca051766089d9faf7b F ext/session/sqlite3session.h cb4d860101ba6d3ac810f18684539b766d24d668fa2436cdde90d711af9464fb F ext/session/test_session.c eb0bd6c1ea791c1d66ee4ef94c16500dad936386 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 @@ -422,7 +422,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a F src/alter.c cf7a8af45cb0ace672f47a1b29ab24092a9e8cd8d945a9974e3b5d925f548594 -F src/analyze.c f9bfffd0416c547a916cb96793b94684bdb0d26a71542ea31819c6757741c19d +F src/analyze.c 6b42e36a5dcc2703a771f2411bd5e99524bd62c7ecde209bb88dfb04c72f046e F src/attach.c 84c477e856b24c2b9a0983b438a707c0cf4d616cee7a425401d418e58afec24c F src/auth.c 6277d63837357549fe14e723490d6dc1a38768d71c795c5eb5c0f8a99f918f73 F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b @@ -1700,7 +1700,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P a8aea925f8fde8f2dc5ff4b744d54aa2bf8916f3ee57f22d77fd1ddb5a35a9cc -R fffe7fea955e558a2fcc7877bcdc39db +P 09aed13678374bf22087cd808808b711dc703b7c18bc8aaf704850611e17f5cd +R 99a2e08bb84b46a9c74125d0aa1dbedc +T *branch * sessions-stat1 +T *sym-sessions-stat1 * +T -sym-trunk * U dan -Z 8da05c1c6303c10ad33f099cfa382df7 +Z 913b883400e9793131764656e068c1a3 diff --git a/manifest.uuid b/manifest.uuid index 41ac115dac..a3bd20f4c8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -09aed13678374bf22087cd808808b711dc703b7c18bc8aaf704850611e17f5cd \ No newline at end of file +25bf734be1b3883fccf12ac4d93d50289aa307fb60a52e0e32df12f7ee4edc7a \ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index 85c603ffdf..0d13d77790 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -1309,6 +1309,9 @@ static void analyzeOneTable( sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + sqlite3VdbeChangeP4(v, -1, (char*)pStat1, P4_TABLE); +#endif sqlite3VdbeJumpHere(v, jZeroRows); } } From 1611e5a301fdda3843759c335f073142fc2dd271 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 18 Jan 2018 15:06:23 +0000 Subject: [PATCH 2/5] Simplify the sessions preupdate-hook logic for transforming NULL to X'' for column sqlite_stat1.idx. FossilOrigin-Name: 089d7cecaaa47db58320b216a111a5e56123d022008be6c81bc0746148bbdb58 --- ext/session/sqlite3session.c | 143 +++++++++++++++++++++++------------ manifest | 15 ++-- manifest.uuid | 2 +- 3 files changed, 103 insertions(+), 57 deletions(-) diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index cbe75d5667..fa7dd677a1 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -46,6 +46,7 @@ struct sqlite3_session { int rc; /* Non-zero if an error has occurred */ void *pFilterCtx; /* First argument to pass to xTableFilter */ int (*xTableFilter)(void *pCtx, const char *zTab); + sqlite3_value *pZeroBlob; /* Value containing X'' */ sqlite3_session *pNext; /* Next session object on same db. */ SessionTable *pTable; /* List of attached tables */ SessionHook hook; /* APIs to grab new and old data with */ @@ -473,35 +474,32 @@ static int sessionPreupdateHash( if( rc!=SQLITE_OK ) return rc; eType = sqlite3_value_type(pVal); - if( pTab->bStat1 && eType==SQLITE_NULL ){ - h = sessionHashAppendType(h, SQLITE_BLOB); - }else{ - h = sessionHashAppendType(h, eType); - if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ - i64 iVal; - if( eType==SQLITE_INTEGER ){ - iVal = sqlite3_value_int64(pVal); - }else{ - double rVal = sqlite3_value_double(pVal); - assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); - memcpy(&iVal, &rVal, 8); - } - h = sessionHashAppendI64(h, iVal); - }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ - const u8 *z; - int n; - if( eType==SQLITE_TEXT ){ - z = (const u8 *)sqlite3_value_text(pVal); - }else{ - z = (const u8 *)sqlite3_value_blob(pVal); - } - n = sqlite3_value_bytes(pVal); - if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; - h = sessionHashAppendBlob(h, n, z); + h = sessionHashAppendType(h, eType); + if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ + i64 iVal; + if( eType==SQLITE_INTEGER ){ + iVal = sqlite3_value_int64(pVal); }else{ - assert( eType==SQLITE_NULL ); - *pbNullPK = 1; + double rVal = sqlite3_value_double(pVal); + assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); + memcpy(&iVal, &rVal, 8); } + h = sessionHashAppendI64(h, iVal); + }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ + const u8 *z; + int n; + if( eType==SQLITE_TEXT ){ + z = (const u8 *)sqlite3_value_text(pVal); + }else{ + z = (const u8 *)sqlite3_value_blob(pVal); + } + n = sqlite3_value_bytes(pVal); + if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; + h = sessionHashAppendBlob(h, n, z); + }else{ + assert( eType==SQLITE_NULL ); + assert( pTab->bStat1==0 || i!=1 ); + *pbNullPK = 1; } } } @@ -555,7 +553,7 @@ static unsigned int sessionChangeHash( || eType==SQLITE_TEXT || eType==SQLITE_BLOB || eType==SQLITE_NULL || eType==0 ); - assert( !isPK || (eType!=0 && (pTab->bStat1 || eType!=SQLITE_NULL)) ); + assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) ); if( isPK ){ a++; @@ -563,7 +561,7 @@ static unsigned int sessionChangeHash( if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ h = sessionHashAppendI64(h, sessionGetI64(a)); a += 8; - }else if( eType!=SQLITE_NULL ){ + }else{ int n; a += sessionVarintGet(a, &n); h = sessionHashAppendBlob(h, n, a); @@ -799,7 +797,6 @@ static int sessionPreupdateEqual( sqlite3_value *pVal; /* Value returned by preupdate_new/old */ int rc; /* Error code from preupdate_new/old */ int eType = *a++; /* Type of value from change record */ - int eValType; /* The following calls to preupdate_new() and preupdate_old() can not ** fail. This is because they cache their return values, and by the @@ -814,14 +811,7 @@ static int sessionPreupdateEqual( rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal); } assert( rc==SQLITE_OK ); - eValType = sqlite3_value_type(pVal); - if( eType==SQLITE_BLOB && eValType==SQLITE_NULL && pTab->bStat1 ){ - int n; - a += sessionVarintGet(a, &n); - if( n!=0 ) return 0; - continue; - } - if( eValType!=eType ) return 0; + if( sqlite3_value_type(pVal)!=eType ) return 0; /* A SessionChange object never has a NULL value in a PK column */ assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT @@ -1068,6 +1058,47 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ return (pSession->rc || pTab->abPK==0); } +/* +** Versions of the four methods in object SessionHook for use with the +** sqlite_stat1 table. The purpose of this is to substitute a zero-length +** blob each time a NULL value is read from the "idx" column of the +** sqlite_stat1 table. +*/ +typedef struct SessionStat1Ctx SessionStat1Ctx; +struct SessionStat1Ctx { + SessionHook hook; + sqlite3_session *pSession; +}; +static int sessionStat1Old(void *pCtx, int iCol, sqlite3_value **ppVal){ + SessionStat1Ctx *p = (SessionStat1Ctx*)pCtx; + sqlite3_value *pVal = 0; + int rc = p->hook.xOld(p->hook.pCtx, iCol, &pVal); + if( rc==SQLITE_OK && iCol==1 && sqlite3_value_type(pVal)==SQLITE_NULL ){ + pVal = p->pSession->pZeroBlob; + } + *ppVal = pVal; + return rc; +} +static int sessionStat1New(void *pCtx, int iCol, sqlite3_value **ppVal){ + SessionStat1Ctx *p = (SessionStat1Ctx*)pCtx; + sqlite3_value *pVal = 0; + int rc = p->hook.xNew(p->hook.pCtx, iCol, &pVal); + if( rc==SQLITE_OK && iCol==1 && sqlite3_value_type(pVal)==SQLITE_NULL ){ + pVal = p->pSession->pZeroBlob; + } + *ppVal = pVal; + return rc; +} +static int sessionStat1Count(void *pCtx){ + SessionStat1Ctx *p = (SessionStat1Ctx*)pCtx; + return p->hook.xCount(p->hook.pCtx); +} +static int sessionStat1Depth(void *pCtx){ + SessionStat1Ctx *p = (SessionStat1Ctx*)pCtx; + return p->hook.xDepth(p->hook.pCtx); +} + + /* ** This function is only called from with a pre-update-hook reporting a ** change on table pTab (attached to session pSession). The type of change @@ -1084,6 +1115,7 @@ static void sessionPreupdateOneChange( int iHash; int bNull = 0; int rc = SQLITE_OK; + SessionStat1Ctx stat1; if( pSession->rc ) return; @@ -1103,13 +1135,32 @@ static void sessionPreupdateOneChange( return; } + if( pTab->bStat1 ){ + stat1.hook = pSession->hook; + stat1.pSession = pSession; + pSession->hook.pCtx = (void*)&stat1; + pSession->hook.xNew = sessionStat1New; + pSession->hook.xOld = sessionStat1Old; + pSession->hook.xCount = sessionStat1Count; + pSession->hook.xDepth = sessionStat1Depth; + if( pSession->pZeroBlob==0 ){ + sqlite3_value *p = sqlite3ValueNew(0); + if( p==0 ){ + rc = SQLITE_NOMEM; + goto error_out; + } + sqlite3ValueSetStr(p, 0, "", 0, SQLITE_STATIC); + pSession->pZeroBlob = p; + } + } + /* Calculate the hash-key for this change. If the primary key of the row ** includes a NULL value, exit early. Such changes are ignored by the ** session module. */ rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull); if( rc!=SQLITE_OK ) goto error_out; - if( bNull==0 || pTab->bStat1 ){ + if( bNull==0 ){ /* Search the hash table for an existing record for this row. */ SessionChange *pC; for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){ @@ -1144,7 +1195,6 @@ static void sessionPreupdateOneChange( rc = sessionSerializeValue(0, p, &nByte); if( rc!=SQLITE_OK ) goto error_out; } - if( pTab->bStat1 ) nByte += 30; /* Allocate the change object */ pChange = (SessionChange *)sqlite3_malloc(nByte); @@ -1168,12 +1218,7 @@ static void sessionPreupdateOneChange( }else if( pTab->abPK[i] ){ pSession->hook.xNew(pSession->hook.pCtx, i, &p); } - if( p && pTab->bStat1 && sqlite3_value_type(p)==SQLITE_NULL ){ - pChange->aRecord[nByte++] = SQLITE_BLOB; - nByte += sessionVarintPut(&pChange->aRecord[nByte], 0); - }else{ - sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte); - } + sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte); } /* Add the change to the hash-table */ @@ -1198,6 +1243,9 @@ static void sessionPreupdateOneChange( /* If an error has occurred, mark the session object as failed. */ error_out: + if( pTab->bStat1 ){ + pSession->hook = stat1.hook; + } if( rc!=SQLITE_OK ){ pSession->rc = rc; } @@ -1659,6 +1707,7 @@ void sqlite3session_delete(sqlite3_session *pSession){ } } sqlite3_mutex_leave(sqlite3_db_mutex(db)); + sqlite3ValueFree(pSession->pZeroBlob); /* Delete all attached table objects. And the contents of their ** associated hash-tables. */ @@ -2188,7 +2237,7 @@ static int sessionSelectBind( switch( eType ){ case 0: case SQLITE_NULL: - /* assert( abPK[i]==0 ); */ + assert( abPK[i]==0 ); break; case SQLITE_INTEGER: { diff --git a/manifest b/manifest index ce17059a62..8c535b9409 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\sin\sthe\ssessions\smodule\swith\slogging\ssqlite_stat1\srows\sfor\swhich\n(idx\sIS\sNULL)\sis\strue. -D 2018-01-17T20:57:20.602 +C Simplify\sthe\ssessions\spreupdate-hook\slogic\sfor\stransforming\sNULL\sto\sX''\sfor\ncolumn\ssqlite_stat1.idx. +D 2018-01-18T15:06:23.750 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 38f84f301cbef443b2d269f67a74b8cc536469831f70df7c3e912acc04932cc2 @@ -400,7 +400,7 @@ F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7 F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0 F ext/session/sessionstat1.test 16268a9bf62ab19c9bc9e41404bf7a13b5fd37c9cb6cf278a472f0c6c50d7ac1 F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc -F ext/session/sqlite3session.c 870d142ec13c0e053139e7f3427aa550e59faf1775ae2bca051766089d9faf7b +F ext/session/sqlite3session.c 55f20fa4a9b6acc338e11867439633c7faa3d18809cdd826fde6abf9a75dd7a0 F ext/session/sqlite3session.h cb4d860101ba6d3ac810f18684539b766d24d668fa2436cdde90d711af9464fb F ext/session/test_session.c eb0bd6c1ea791c1d66ee4ef94c16500dad936386 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 @@ -1700,10 +1700,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 09aed13678374bf22087cd808808b711dc703b7c18bc8aaf704850611e17f5cd -R 99a2e08bb84b46a9c74125d0aa1dbedc -T *branch * sessions-stat1 -T *sym-sessions-stat1 * -T -sym-trunk * +P 25bf734be1b3883fccf12ac4d93d50289aa307fb60a52e0e32df12f7ee4edc7a +R f7c998dd2622eae2189b630c53aa0780 U dan -Z 913b883400e9793131764656e068c1a3 +Z 3f8bd6ff445f731b364c070e53a9b8ca diff --git a/manifest.uuid b/manifest.uuid index a3bd20f4c8..0823a466b2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -25bf734be1b3883fccf12ac4d93d50289aa307fb60a52e0e32df12f7ee4edc7a \ No newline at end of file +089d7cecaaa47db58320b216a111a5e56123d022008be6c81bc0746148bbdb58 \ No newline at end of file From d1cccf19b5fd00ed45613d4f14dde59024942158 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 18 Jan 2018 16:42:11 +0000 Subject: [PATCH 3/5] Fix sessions module conflict handling for the sqlite_stat1 table. FossilOrigin-Name: f05ee74e05c401eb075a1ba65179662a08a5c3d5b55fc81f2acc841e841dd055 --- ext/session/sessionstat1.test | 77 +++++++++++++++++++++++++++++++++++ ext/session/sqlite3session.c | 41 +++++++++++++------ manifest | 14 +++---- manifest.uuid | 2 +- 4 files changed, 114 insertions(+), 20 deletions(-) diff --git a/ext/session/sessionstat1.test b/ext/session/sessionstat1.test index 22b2de8dbc..082c02bcf1 100644 --- a/ext/session/sessionstat1.test +++ b/ext/session/sessionstat1.test @@ -226,9 +226,86 @@ do_scc_test 3.7.1 { {INSERT sqlite_stat1 0 XX. {} {t t2 b {} t 6}} } +#------------------------------------------------------------------------- +catch { db2 close } +reset_db +forcedelete test.db2 +sqlite3 db2 test.db2 +do_test 4.1.0 { + do_common_sql { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a); + CREATE INDEX i2 ON t1(b); + INSERT INTO t1 VALUES(1,1), (2,2); + ANALYZE; + } + execsql { DELETE FROM sqlite_stat1 } +} {} +do_test 4.1.1 { + execsql { INSERT INTO t1 VALUES(3,3); } + set C [changeset_from_sql {ANALYZE}] + set ::c [list] + proc xConflict {args} { + lappend ::c $args + return "OMIT" + } + sqlite3changeset_apply db2 $C xConflict + set ::c +} [list {*}{ + {INSERT sqlite_stat1 CONFLICT {t t1 t i1 t {3 1}} {t t1 t i1 t {2 1}}} + {INSERT sqlite_stat1 CONFLICT {t t1 t i2 t {3 1}} {t t1 t i2 t {2 1}}} +}] +do_execsql_test -db db2 4.1.2 { + SELECT * FROM sqlite_stat1 ORDER BY 1,2; +} {t1 i1 {2 1} t1 i2 {2 1}} + +do_test 4.1.3 { + proc xConflict {args} { + return "REPLACE" + } + sqlite3changeset_apply db2 $C xConflict + execsql { SELECT * FROM sqlite_stat1 ORDER BY 1,2 } db2 +} {t1 i1 {3 1} t1 i2 {3 1}} + +do_test 4.2.0 { + do_common_sql { + DROP TABLE t1; + CREATE TABLE t3(x,y); + INSERT INTO t3 VALUES('a','a'); + INSERT INTO t3 VALUES('b','b'); + ANALYZE; + } + execsql { DELETE FROM sqlite_stat1 } +} {} +do_test 4.2.1 { + execsql { INSERT INTO t3 VALUES('c','c'); } + set C [changeset_from_sql {ANALYZE}] + set ::c [list] + proc xConflict {args} { + lappend ::c $args + return "OMIT" + } + sqlite3changeset_apply db2 $C xConflict + set ::c +} [list {*}{ + {INSERT sqlite_stat1 CONFLICT {t t3 b {} t 3} {t t3 b {} t 2}} +}] + +db2 null null +do_execsql_test -db db2 4.2.2 { + SELECT * FROM sqlite_stat1 ORDER BY 1,2; +} {t3 null 2} + +do_test 4.2.3 { + proc xConflict {args} { + return "REPLACE" + } + sqlite3changeset_apply db2 $C xConflict + execsql { SELECT * FROM sqlite_stat1 ORDER BY 1,2 } db2 +} {t3 null 3} finish_test diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index fa7dd677a1..ec1a93e08e 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -3378,7 +3378,7 @@ struct SessionApplyCtx { int nCol; /* Size of azCol[] and abPK[] arrays */ const char **azCol; /* Array of column names */ u8 *abPK; /* Boolean array - true if column is in PK */ - + int bStat1; /* True if table is sqlite_stat1 */ int bDeferConstraints; /* True to defer constraints */ SessionBuffer constraints; /* Deferred constraints are stored here */ }; @@ -3981,11 +3981,25 @@ static int sessionApplyOneOp( }else{ assert( op==SQLITE_INSERT ); - rc = sessionBindRow(pIter, sqlite3changeset_new, nCol, 0, p->pInsert); - if( rc!=SQLITE_OK ) return rc; + if( p->bStat1 ){ + /* Check if there is a conflicting row. For sqlite_stat1, this needs + ** to be done using a SELECT, as there is no PRIMARY KEY in the + ** database schema to throw an exception if a duplicate is inserted. */ + rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect); + if( rc==SQLITE_ROW ){ + rc = SQLITE_CONSTRAINT; + sqlite3_reset(p->pSelect); + } + } + + if( rc==SQLITE_OK ){ + rc = sessionBindRow(pIter, sqlite3changeset_new, nCol, 0, p->pInsert); + if( rc!=SQLITE_OK ) return rc; + + sqlite3_step(p->pInsert); + rc = sqlite3_reset(p->pInsert); + } - sqlite3_step(p->pInsert); - rc = sqlite3_reset(p->pInsert); if( (rc&0xff)==SQLITE_CONSTRAINT ){ rc = sessionConflictHandler( SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, pbReplace @@ -4222,13 +4236,16 @@ static int sessionChangesetApply( if( (rc = sessionStat1Sql(db, &sApply) ) ){ break; } - }else - if((rc = sessionSelectRow(db, zTab, &sApply)) - || (rc = sessionUpdateRow(db, zTab, &sApply)) - || (rc = sessionDeleteRow(db, zTab, &sApply)) - || (rc = sessionInsertRow(db, zTab, &sApply)) - ){ - break; + sApply.bStat1 = 1; + }else{ + if((rc = sessionSelectRow(db, zTab, &sApply)) + || (rc = sessionUpdateRow(db, zTab, &sApply)) + || (rc = sessionDeleteRow(db, zTab, &sApply)) + || (rc = sessionInsertRow(db, zTab, &sApply)) + ){ + break; + } + sApply.bStat1 = 0; } } nTab = sqlite3Strlen30(zTab); diff --git a/manifest b/manifest index 8c535b9409..1dd8d9aec3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplify\sthe\ssessions\spreupdate-hook\slogic\sfor\stransforming\sNULL\sto\sX''\sfor\ncolumn\ssqlite_stat1.idx. -D 2018-01-18T15:06:23.750 +C Fix\ssessions\smodule\sconflict\shandling\sfor\sthe\ssqlite_stat1\stable. +D 2018-01-18T16:42:11.152 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 38f84f301cbef443b2d269f67a74b8cc536469831f70df7c3e912acc04932cc2 @@ -398,9 +398,9 @@ F ext/session/sessionat.test efe88965e74ff1bc2af9c310b28358c02d420c1fb2705cc7a28 F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7 F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0 -F ext/session/sessionstat1.test 16268a9bf62ab19c9bc9e41404bf7a13b5fd37c9cb6cf278a472f0c6c50d7ac1 +F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc -F ext/session/sqlite3session.c 55f20fa4a9b6acc338e11867439633c7faa3d18809cdd826fde6abf9a75dd7a0 +F ext/session/sqlite3session.c 989466bba4dff0ede8d4c450b1fc65ca222b87e31193eddbf3931b88bf898a57 F ext/session/sqlite3session.h cb4d860101ba6d3ac810f18684539b766d24d668fa2436cdde90d711af9464fb F ext/session/test_session.c eb0bd6c1ea791c1d66ee4ef94c16500dad936386 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 @@ -1700,7 +1700,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 25bf734be1b3883fccf12ac4d93d50289aa307fb60a52e0e32df12f7ee4edc7a -R f7c998dd2622eae2189b630c53aa0780 +P 089d7cecaaa47db58320b216a111a5e56123d022008be6c81bc0746148bbdb58 +R b26168110a43028fdb5773dcda862810 U dan -Z 3f8bd6ff445f731b364c070e53a9b8ca +Z 3667003c7f66cdcf6e4cbccaf82894fb diff --git a/manifest.uuid b/manifest.uuid index 0823a466b2..fd362a9c44 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -089d7cecaaa47db58320b216a111a5e56123d022008be6c81bc0746148bbdb58 \ No newline at end of file +f05ee74e05c401eb075a1ba65179662a08a5c3d5b55fc81f2acc841e841dd055 \ No newline at end of file From e3ca3831bae7ae6f7a7ba2d858f8bf95d6e1af83 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 18 Jan 2018 16:53:41 +0000 Subject: [PATCH 4/5] Add comments describing the special sqlite_stat1 handling to sqlite3session.h. FossilOrigin-Name: 4431a3256f7436e34b3c33edc1f3e53df7eb3c87daec9bac8d038895d93ca7f2 --- ext/session/sqlite3session.h | 24 ++++++++++++++++++++++++ manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index b22df129cb..23d8e689fc 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -147,6 +147,30 @@ int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect); ** ** SQLITE_OK is returned if the call completes without error. Or, if an error ** occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned. +** +**

Special sqlite_stat1 Handling

+** +** The "sqlite_stat1" table is an exception to some of the rules above. In +** SQLite, the schema of sqlite_stat1 is: +**
+**        CREATE TABLE sqlite_stat1(tbl,idx,stat)  
+**  
+** +** Even though sqlite_stat1 does not have a PRIMARY KEY, changes are +** recorded for it as if the PRIMARY KEY is (tbl,idx). Additionally, changes +** are recorded for rows for which (idx IS NULL) is true. However, for such +** rows a zero-length blob (SQL value X'') is stored in the changeset or +** patchset instead of a NULL value. This allows such changesets to be +** manipulated by legacy implementations of sqlite3changeset_invert(), +** concat() and similar. +** +** The sqlite3changeset_apply() function automatically converts the +** zero-length blob back to a NULL value when updating the sqlite_stat1 +** table. However, if the application calls sqlite3changeset_new(), +** sqlite3changeset_old() or sqlite3changeset_conflict on a changeset +** iterator directly (including on a changeset iterator passed to a +** conflict-handler callback) then the X'' value is returned. The application +** must translate X'' to NULL itself if required. */ int sqlite3session_attach( sqlite3_session *pSession, /* Session object */ diff --git a/manifest b/manifest index 1dd8d9aec3..5cb2ef0657 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\ssessions\smodule\sconflict\shandling\sfor\sthe\ssqlite_stat1\stable. -D 2018-01-18T16:42:11.152 +C Add\scomments\sdescribing\sthe\sspecial\ssqlite_stat1\shandling\sto\ssqlite3session.h. +D 2018-01-18T16:53:41.289 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 38f84f301cbef443b2d269f67a74b8cc536469831f70df7c3e912acc04932cc2 @@ -401,7 +401,7 @@ F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0 F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc F ext/session/sqlite3session.c 989466bba4dff0ede8d4c450b1fc65ca222b87e31193eddbf3931b88bf898a57 -F ext/session/sqlite3session.h cb4d860101ba6d3ac810f18684539b766d24d668fa2436cdde90d711af9464fb +F ext/session/sqlite3session.h 1bf32a9598ae4288bf7ecbda032a0479d6225ab8d2283fd252056a26b1046946 F ext/session/test_session.c eb0bd6c1ea791c1d66ee4ef94c16500dad936386 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 @@ -1700,7 +1700,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 089d7cecaaa47db58320b216a111a5e56123d022008be6c81bc0746148bbdb58 -R b26168110a43028fdb5773dcda862810 +P f05ee74e05c401eb075a1ba65179662a08a5c3d5b55fc81f2acc841e841dd055 +R 62c81de92f4cf24f6460f153aeb30b06 U dan -Z 3667003c7f66cdcf6e4cbccaf82894fb +Z 4f9e979274bbe8dc16dfd6dd12c05029 diff --git a/manifest.uuid b/manifest.uuid index fd362a9c44..699a6f987c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f05ee74e05c401eb075a1ba65179662a08a5c3d5b55fc81f2acc841e841dd055 \ No newline at end of file +4431a3256f7436e34b3c33edc1f3e53df7eb3c87daec9bac8d038895d93ca7f2 \ No newline at end of file From cae5b9feacb655ede761d15cc00d37b82e35b5b2 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 18 Jan 2018 16:56:19 +0000 Subject: [PATCH 5/5] Clarify the handling of the sqlite_stat1 table by legacy versions of the sessions module. FossilOrigin-Name: dc7c48cb4126db9e25c73512cc743155293fe1c4c2516f8c84102228695b6e70 --- ext/session/sqlite3session.h | 9 +++++++-- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index 23d8e689fc..9f33855df0 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -150,8 +150,8 @@ int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect); ** **

Special sqlite_stat1 Handling

** -** The "sqlite_stat1" table is an exception to some of the rules above. In -** SQLite, the schema of sqlite_stat1 is: +** As of SQLite version 3.22.0, the "sqlite_stat1" table is an exception to +** some of the rules above. In SQLite, the schema of sqlite_stat1 is: **
 **        CREATE TABLE sqlite_stat1(tbl,idx,stat)  
 **  
@@ -171,6 +171,11 @@ int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect); ** iterator directly (including on a changeset iterator passed to a ** conflict-handler callback) then the X'' value is returned. The application ** must translate X'' to NULL itself if required. +** +** Legacy (older than 3.22.0) versions of the sessions module cannot capture +** changes made to the sqlite_stat1 table. Legacy versions of the +** sqlite3changeset_apply() function silently ignore any modifications to the +** sqlite_stat1 table that are part of a changeset or patchset. */ int sqlite3session_attach( sqlite3_session *pSession, /* Session object */ diff --git a/manifest b/manifest index 5cb2ef0657..44a95be5ce 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\scomments\sdescribing\sthe\sspecial\ssqlite_stat1\shandling\sto\ssqlite3session.h. -D 2018-01-18T16:53:41.289 +C Clarify\sthe\shandling\sof\sthe\ssqlite_stat1\stable\sby\slegacy\sversions\sof\sthe\nsessions\smodule. +D 2018-01-18T16:56:19.115 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 38f84f301cbef443b2d269f67a74b8cc536469831f70df7c3e912acc04932cc2 @@ -401,7 +401,7 @@ F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0 F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc F ext/session/sqlite3session.c 989466bba4dff0ede8d4c450b1fc65ca222b87e31193eddbf3931b88bf898a57 -F ext/session/sqlite3session.h 1bf32a9598ae4288bf7ecbda032a0479d6225ab8d2283fd252056a26b1046946 +F ext/session/sqlite3session.h 01774161cbd328fe3d496323655b9cc142317ff1fb1ae15c1232075ea240e3a4 F ext/session/test_session.c eb0bd6c1ea791c1d66ee4ef94c16500dad936386 F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 @@ -1700,7 +1700,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f05ee74e05c401eb075a1ba65179662a08a5c3d5b55fc81f2acc841e841dd055 -R 62c81de92f4cf24f6460f153aeb30b06 +P 4431a3256f7436e34b3c33edc1f3e53df7eb3c87daec9bac8d038895d93ca7f2 +R 6488443bbec455d1afdebff112c6cb1b U dan -Z 4f9e979274bbe8dc16dfd6dd12c05029 +Z 8531e6daca6e4a95660fec10c24747c3 diff --git a/manifest.uuid b/manifest.uuid index 699a6f987c..ecf7bc63d6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4431a3256f7436e34b3c33edc1f3e53df7eb3c87daec9bac8d038895d93ca7f2 \ No newline at end of file +dc7c48cb4126db9e25c73512cc743155293fe1c4c2516f8c84102228695b6e70 \ No newline at end of file