From: drh Date: Mon, 11 Jun 2018 17:35:02 +0000 (+0000) Subject: Add the OP_SetTabCol and OP_VerifyTabCol opcodes, only when compiling with X-Git-Tag: version-3.25.0~196 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=299bf7c2f0193e2d39dd96ad4ccbe673ae9e21be;p=thirdparty%2Fsqlite.git Add the OP_SetTabCol and OP_VerifyTabCol opcodes, only when compiling with SQLITE_DEBUG, to do run-time verification of the column cache. FossilOrigin-Name: b37614a3670770919a7c7f8157c5fd6578d736447998640b52e5cef8174cadc2 --- diff --git a/manifest b/manifest index 6170904da3..3228fbdad9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Strengthen\sthe\ssqlite3VdbeMemAboutToChange()\srun-time\sverification\smechanism\nto\sbetter\sdetect\smissed\scalls\sto\sAboutToChange(). -D 2018-06-11T13:10:45.872 +C Add\sthe\sOP_SetTabCol\sand\sOP_VerifyTabCol\sopcodes,\sonly\swhen\scompiling\swith\nSQLITE_DEBUG,\sto\sdo\srun-time\sverification\sof\sthe\scolumn\scache. +D 2018-06-11T17:35:02.447 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da @@ -447,7 +447,7 @@ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957 F src/dbpage.c 4aa7f26198934dbd002e69418220eae3dbc71b010bbac32bd78faf86b52ce6c3 F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91 F src/delete.c 4c8c7604277a2041647f96b78f4b9a47858e9217e4fb333d35e7b5ab32c5b57f -F src/expr.c 16f90ae2af2a100bc430a89184afde54878d82f18267e8d00bc4f33e695a7c57 +F src/expr.c 3ab855fb882d8758220edaf261d8d56ad2cb3736f92c818f4ae250ac4a2752a4 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c b1da9ef8dc834603bb0d28972378a7ce65897847f9a1e89ab800bbdf24c788ee F src/func.c e2e3c02621a528a472933fd4733a5da635676f1461be73293f6e9f62f18d4eaa @@ -500,7 +500,7 @@ F src/shell.c.in 4d0ddf10c403710d241bf920163dcf032c21119aebb61e70840942c0eafecdf F src/sqlite.h.in 63b07f76731f2b1e55c48fdb9f0508dcc6fbe3971010b8612ffd847c3c56d9a1 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7 -F src/sqliteInt.h 9332b820382288dde7d6ea712bbcd34380bbbeb44f6f7032710bd5240fd4067d +F src/sqliteInt.h c8d304712b6b4e2150cbb5355b1a80427eee7b3ea1a6b5827afadf557360d7fe F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@ -565,13 +565,13 @@ F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88 F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5 F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157 F src/vacuum.c 36e7d21a20c0bf6ef4ef7c399d192b5239410b7c4d3c1070fba4e30810d0b855 -F src/vdbe.c d7a475cec51c83daf0d348301a1aec77f535832ea946632b5738ff9f087c0edb -F src/vdbe.h c58fa2d8fff2a235a685687340ee555269f5421e4ec2ee758a5550961d3b25a4 -F src/vdbeInt.h d299d7a19853463dac418de0d97f2dd9cb4ddb495a45c93364e2daee109ba0ef +F src/vdbe.c e0ac1d7cff72b4e8b250c5e698f0fc43f0c8b5a8f35654716d8ab1fa89db669e +F src/vdbe.h 9c8c245fa3785266c269ab02c135c836ff49a307612186686bcdae500f409945 +F src/vdbeInt.h 8ebc37ead2cfde92a63d74e3940fe0a94d456078c77e6cefbc2ecf15630f9fda F src/vdbeapi.c 765a0bbe01311626417de6cb743f7f25f9f98435c98a9df4bb0714d11014633d -F src/vdbeaux.c ea0b7821dd74068cb8b232d1ba3dc457ecaca302d2cf67e1f24ff3bcc7d94c52 +F src/vdbeaux.c 0e7af8614c87dd25bc2395d22c847deba96ff87d19758d5cd7f459a466f14cbb F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191 -F src/vdbemem.c 8f5cd24151c87294c2fb31c9e9353b074786c10cb9e7ef74d93c2e1ec85d1ee1 +F src/vdbemem.c 613cbbcca8d21c66df311fc17f9460180abcf7a909e12493618fd570f9858036 F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f F src/vdbetrace.c 79d6dbbc479267b255a7de8080eee6e729928a0ef93ed9b0bfa5618875b48392 F src/vtab.c 10ea07dec111de6fb0a4fc87a7ffa4c65fdc088a19dbfaf7d6f2b128f2e8eb7b @@ -579,7 +579,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c d44a0811afd2155b1157c38b33141d4ac028fda6232485bed664015bb05819ca F src/wal.h 8de5d2d3de0956d6f6cb48c83a4012d5f227b8fe940f3a349a4b7e85ebcb492a F src/walker.c da987a20d40145c0a03c07d8fefcb2ed363becc7680d0500d9c79915591f5b1f -F src/where.c fe1a6f97c12cc9472ccce86166ba3f827cf61d6ae770c036a6396b63863baac4 +F src/where.c 0bcbf9e191ca07f9ea2008aa80e70ded46bcdffd26560c83397da501f00aece6 F src/whereInt.h b90ef9b9707ef750eab2a7a080c48fb4900315033274689def32d0cf5a81ebe4 F src/wherecode.c 3317f2b083a66d3e65a03edf316ade4ccb0a99c9956273282ebb579b95d4ba96 F src/whereexpr.c 19cf35cdd9bf6d5589d8a5c960d99259761136187a2319a6e14d11cf1abe14c2 @@ -1731,7 +1731,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 9faf41713e865f26485d9e90918d77c25be211384941eadda6a36237f52c25de -R 846e9acb3549bc01ad42c2408534b9d1 +P 793e942205a12eedb7ecc5ad8a27e3e52bbd4e1d50a0d1453d04a83ba728884e +R e32799ffc139aea23a34579bcd80eade U drh -Z 5f45d155d5f89ae268c81c641f0e97a0 +Z 5651f63d60929e8aa5ef5f44ca04cc41 diff --git a/manifest.uuid b/manifest.uuid index 5504e2c63e..b1328ceffb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -793e942205a12eedb7ecc5ad8a27e3e52bbd4e1d50a0d1453d04a83ba728884e \ No newline at end of file +b37614a3670770919a7c7f8157c5fd6578d736447998640b52e5cef8174cadc2 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 91eb44b8eb..1ddcca819f 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3200,6 +3200,13 @@ void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int iReg){ } #endif +#ifdef SQLITE_DEBUG_COLUMNCACHE + /* Add a SetTabCol opcode for run-time verification that the column + ** cache is working correctly. + */ + sqlite3VdbeAddOp3(pParse->pVdbe, OP_SetTabCol, iTab, iCol, iReg); +#endif + /* If the cache is already full, delete the least recently used entry */ if( pParse->nColCache>=SQLITE_N_COLCACHE ){ minLru = 0x7fffffff; @@ -3373,6 +3380,9 @@ int sqlite3ExprCodeGetColumn( if( p->iTable==iTable && p->iColumn==iColumn ){ p->lru = pParse->iCacheCnt++; sqlite3ExprCachePinRegister(pParse, p->iReg); +#ifdef SQLITE_DEBUG_COLUMNCACHE + sqlite3VdbeAddOp3(v, OP_VerifyTabCol, iTable, iColumn, p->iReg); +#endif return p->iReg; } } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 98bd7e4079..6180467005 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -367,6 +367,17 @@ # undef NDEBUG #endif +/* SQLITE_DEBUG_COLUMNCACHE is synomous with SQLITE_DEBUG. The +** SQLITE_DEBUG_COLUMNCACHE symbol only exists to provide a convenient +** way to search for all code that deals with verifying correct behavior +** of the column cache. +*/ +#ifdef SQLITE_DEBUG +# define SQLITE_DEBUG_COLUMNCACHE 1 +#else +# undef SQLIT_DEBUG_COLUMNCACHE +#endif + /* ** Enable SQLITE_ENABLE_EXPLAIN_COMMENTS if SQLITE_DEBUG is turned on. */ diff --git a/src/vdbe.c b/src/vdbe.c index 243e4fe70a..f65068672c 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -36,6 +36,18 @@ # define memAboutToChange(P,M) #endif +/* +** Given a cursor number and a column for a table or index, compute a +** hash value for use in the Mem.iTabColHash value. The iTabColHash +** column is only used for verification - it is omitted from production +** builds. Collisions are harmless in the sense that the correct answer +** still results. The only harm of collisions is that they can potential +** reduce column-cache error detection during SQLITE_DEBUG builds. +** +** No valid hash should be 0. +*/ +#define TableColumnHash(T,C) (((u32)(T)<<16)^(u32)(C+2)) + /* ** The following global variable is incremented every time a cursor ** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test @@ -7318,6 +7330,34 @@ case OP_Abortable: { } #endif +#ifdef SQLITE_DEBUG_COLUMNCACHE +/* Opcode: SetTabCol P1 P2 P3 * * +** +** Set a flag in register REG[P3] indicating that it holds the value +** of column P2 from the table on cursor P1. This flag is checked +** by a subsequent VerifyTabCol opcode. +** +** This opcode only appears SQLITE_DEBUG builds. It is used to verify +** that the expression table column cache is working correctly. +*/ +case OP_SetTabCol: { + aMem[pOp->p3].iTabColHash = TableColumnHash(pOp->p1,pOp->p2); + break; +} +/* Opcode: VerifyTabCol P1 P2 P3 * * +** +** Verify that register REG[P3] contains the value of column P2 from +** cursor P1. Assert() if this is not the case. +** +** This opcode only appears SQLITE_DEBUG builds. It is used to verify +** that the expression table column cache is working correctly. +*/ +case OP_VerifyTabCol: { + assert( aMem[pOp->p3].iTabColHash == TableColumnHash(pOp->p1,pOp->p2) ); + break; +} +#endif + /* Opcode: Noop * * * * * ** ** Do nothing. This instruction is often useful as a jump diff --git a/src/vdbe.h b/src/vdbe.h index b7f960f766..51995e384c 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -343,4 +343,8 @@ void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*); # define sqlite3VdbeScanStatus(a,b,c,d,e) #endif +#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) +void sqlite3VdbePrintOp(FILE*, int, VdbeOp*); +#endif + #endif /* SQLITE_VDBE_H */ diff --git a/src/vdbeInt.h b/src/vdbeInt.h index dd8e29108a..c883ce8b50 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -211,6 +211,9 @@ struct sqlite3_value { Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */ #endif +#ifdef SQLITE_DEBUG_COLUMNCACHE + u32 iTabColHash; /* Hash of table.column that is origin of this value */ +#endif }; /* @@ -449,9 +452,6 @@ void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); void sqliteVdbePopStack(Vdbe*,int); int sqlite3VdbeCursorMoveto(VdbeCursor**, int*); int sqlite3VdbeCursorRestore(VdbeCursor*); -#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) -void sqlite3VdbePrintOp(FILE*, int, Op*); -#endif u32 sqlite3VdbeSerialTypeLen(u32); u8 sqlite3VdbeOneByteSerialTypeLen(u8); u32 sqlite3VdbeSerialType(Mem*, int, u32*); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index c0984317b9..856cad7649 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1613,7 +1613,7 @@ void sqlite3VdbeLeave(Vdbe *p){ /* ** Print a single opcode. This routine is used for debugging only. */ -void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){ +void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){ char *zP4; char zPtr[50]; char zCom[100]; diff --git a/src/vdbemem.c b/src/vdbemem.c index 4759823417..5ce4bca79d 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -905,6 +905,9 @@ void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ } } pMem->pScopyFrom = 0; +#ifdef SQLITE_DEBUG_COLUMN_CACHE + pMem->iTabColHash = 0; +#endif } #endif /* SQLITE_DEBUG */ @@ -925,6 +928,9 @@ void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){ assert( pTo->db==pFrom->db ); if( VdbeMemDynamic(pTo) ){ vdbeClrCopy(pTo,pFrom,srcType); return; } memcpy(pTo, pFrom, MEMCELLSIZE); +#ifdef SQLITE_DEBUG_COLUMNCACHE + pTo->iTabColHash = pFrom->iTabColHash; +#endif if( (pFrom->flags&MEM_Static)==0 ){ pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Ephem); assert( srcType==MEM_Ephem || srcType==MEM_Static ); @@ -942,6 +948,9 @@ int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){ assert( (pFrom->flags & MEM_RowSet)==0 ); if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo); memcpy(pTo, pFrom, MEMCELLSIZE); +#ifdef SQLITE_DEBUG_COLUMNCACHE + pTo->iTabColHash = pFrom->iTabColHash; +#endif pTo->flags &= ~MEM_Dyn; if( pTo->flags&(MEM_Str|MEM_Blob) ){ if( 0==(pFrom->flags&MEM_Static) ){ diff --git a/src/where.c b/src/where.c index 0e47dbf7ee..7d53715923 100644 --- a/src/where.c +++ b/src/where.c @@ -5033,6 +5033,26 @@ whereBeginError: return 0; } +/* +** Part of sqlite3WhereEnd() will rewrite opcodes to reference the +** index rather than the main table. In SQLITE_DEBUG mode, we want +** to trace those changes if PRAGMA vdbe_addoptrace=on. This routine +** does that. +*/ +#ifndef SQLITE_DEBUG +# define OpcodeRewriteTrace(D,K,P) /* no-op */ +#else +# define OpcodeRewriteTrace(D,K,P) sqlite3WhereOpcodeRewriteTrace(D,K,P) + static void sqlite3WhereOpcodeRewriteTrace( + sqlite3 *db, + int pc, + VdbeOp *pOp + ){ + if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return; + sqlite3VdbePrintOp(0, pc, pOp); + } +#endif + /* ** Generate the end of the WHERE loop. See comments on ** sqlite3WhereBegin() for additional information. @@ -5201,6 +5221,11 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ ){ last = sqlite3VdbeCurrentAddr(v); k = pLevel->addrBody; +#ifdef SQLITE_DEBUG + if( db->flags & SQLITE_VdbeAddopTrace ){ + printf("TRANSLATE opcodes in range %d..%d\n", k, last-1); + } +#endif pOp = sqlite3VdbeGetOp(v, k); for(; kp1!=pLevel->iTabCur ) continue; @@ -5220,16 +5245,22 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ if( x>=0 ){ pOp->p2 = x; pOp->p1 = pLevel->iIdxCur; + OpcodeRewriteTrace(db, k, pOp); } assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 || pWInfo->eOnePass ); }else if( pOp->opcode==OP_Rowid ){ pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; + OpcodeRewriteTrace(db, k, pOp); }else if( pOp->opcode==OP_IfNullRow ){ pOp->p1 = pLevel->iIdxCur; + OpcodeRewriteTrace(db, k, pOp); } } +#ifdef SQLITE_DEBUG + if( db->flags & SQLITE_VdbeAddopTrace ) printf("TRANSLATE complete\n"); +#endif } }