From 1611e5a301fdda3843759c335f073142fc2dd271 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 18 Jan 2018 15:06:23 +0000 Subject: [PATCH] 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