From 9e92a47bdfaba67dabd0e26db941e307ecd4237c Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 27 Jun 2013 17:40:30 +0000 Subject: [PATCH] Make sure that sqlite3_stmt_readonly reports false for PRAGMA journal_mode and PRAGMA wal_checkpoint. Ticket [a589ec069e3]. Also keep track of whether a prepared statement does no reading or writing. FossilOrigin-Name: 1937fd8eec2c1da95a782d9dc63926d846b06bdc --- manifest | 24 ++++++++++++------------ manifest.uuid | 2 +- src/sqliteInt.h | 1 + src/vdbe.c | 21 ++++++++++++++++++++- src/vdbeInt.h | 3 ++- src/vdbeapi.c | 1 + src/vdbeaux.c | 22 +++++++++++++++++----- test/capi3d.test | 7 +++++++ 8 files changed, 61 insertions(+), 20 deletions(-) diff --git a/manifest b/manifest index db6e3038a9..06ae3bdb2c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\stest\sto\sensure\sthat\sif\sBEGIN\sIMMEDIATE\sfails\swith\sSQLITE_BUSY,\sit\sdoes\snot\sleave\sthe\suser\swith\san\sopen\sread\stransaction\s(unless\sone\swas\salready\sopen). -D 2013-06-27T14:24:15.136 +C Make\ssure\sthat\ssqlite3_stmt_readonly\sreports\sfalse\sfor\nPRAGMA\sjournal_mode\sand\sPRAGMA\swal_checkpoint.\s\sTicket\s[a589ec069e3].\nAlso\skeep\strack\sof\swhether\sa\sprepared\sstatement\sdoes\sno\sreading\sor\nwriting. +D 2013-06-27T17:40:30.228 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -221,7 +221,7 @@ F src/shell.c c0f38cee126d1ea82275195933359e91d90196a0 F src/sqlite.h.in 9e8d57aa4d2fdc181dc25e9aa295f5ecec7e184a F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3ext.h d936f797812c28b81b26ed18345baf8db28a21a5 -F src/sqliteInt.h e6f069b07fdef1ab54034940b7a6e7be2b4efd57 +F src/sqliteInt.h e8d5ed8a3a1d6be304c3ac7c72b5bd5f4b62a1b0 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -277,11 +277,11 @@ F src/update.c 8e76c3d03e4b7b21cb250bd2df0c05e12993e577 F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f F src/util.c f566b5138099a2df8533b190d0dcc74b7dfbe0c9 F src/vacuum.c ddf21cc9577c4cb459d08bee9863a78ec000c5bb -F src/vdbe.c af2bc360189ce8eaed47133037e082a1652bc4a0 +F src/vdbe.c f678101139d174e92eec1cb21201646ee3ee3383 F src/vdbe.h b52887278cb173e66188da84dfab216bea61119d -F src/vdbeInt.h b8a76f7b7304abfdb8e6bd286eba954ecfe73863 -F src/vdbeapi.c 0b2c78797058c6c9bfa1687977de039566e22877 -F src/vdbeaux.c af9cd9372c7ab82294b9645723e652c2d4213b95 +F src/vdbeInt.h 9735fdf28b8d250e001e5b406f7011a40888c739 +F src/vdbeapi.c be5e9ebd51621571225c54516eb09d97569ff2ff +F src/vdbeaux.c 727ad6060db0e0d3c06581281d8637ed24ba5647 F src/vdbeblob.c 5dc79627775bd9a9b494dd956e26297946417d69 F src/vdbemem.c 833005f1cbbf447289f1973dba2a0c2228c7b8ab F src/vdbesort.c 3937e06b2a0e354500e17dc206ef4c35770a5017 @@ -359,7 +359,7 @@ F test/capi2.test e8b18cc61090b6e5e388f54d6b125d711d1b265a F test/capi3.test 56ab450125ead38846cbae7e5b6a216686c3cffa F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4 F test/capi3c.test 93d24621c9ff84da9da060f30431e0453db1cdb0 -F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1 +F test/capi3d.test 6d0fc0a86d73f42dd19a7d8b7761ab9bc02277d0 F test/capi3e.test ad90088b18b0367125ff2d4b5400153fd2f99aab F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3 F test/check.test 2eb93611139a7dfaed3be80067c7dc5ceb5fb287 @@ -1098,7 +1098,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 4c02b344f5c6f6fb1c61b79d51063a1e0e2d75c0 -R f7e72c4156e7c539662844a39b9e3b02 -U dan -Z d4c7b57dd14fca50abddc9b4b3e495d3 +P 22bced36f0f121f29d94f1f9eab45c8731517a53 +R 182a3fd649ef7d4b97150fcbb047e7a6 +U drh +Z aa0f35d707430a5d3fece648b987ad5a diff --git a/manifest.uuid b/manifest.uuid index deb56ed743..304991bfea 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -22bced36f0f121f29d94f1f9eab45c8731517a53 \ No newline at end of file +1937fd8eec2c1da95a782d9dc63926d846b06bdc \ No newline at end of file diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 0a661937ff..dd626303f7 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -898,6 +898,7 @@ struct sqlite3 { u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ } init; int activeVdbeCnt; /* Number of VDBEs currently executing */ + int noIOVdbeCnt; /* Nr of active VDBEs with no FROM clause */ int writeVdbeCnt; /* Number of active VDBEs that are writing */ int vdbeExecCnt; /* Number of nested calls to VdbeExec() */ int nExtension; /* Number of loaded extensions */ diff --git a/src/vdbe.c b/src/vdbe.c index 81b40628e1..e211ecec92 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -579,6 +579,7 @@ int sqlite3VdbeExec( goto no_mem; } assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY ); + assert( p->noIO==0 || p->readOnly!=0 ); p->rc = SQLITE_OK; assert( p->explain==0 ); p->pResultSet = 0; @@ -2934,6 +2935,8 @@ case OP_AutoCommit: { case OP_Transaction: { Btree *pBt; + assert( p->noIO==0 ); + assert( p->readOnly==0 || pOp->p2==0 ); assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; @@ -2990,6 +2993,7 @@ case OP_ReadCookie: { /* out2-prerelease */ int iDb; int iCookie; + assert( p->noIO==0 ); iDb = pOp->p1; iCookie = pOp->p3; assert( pOp->p3p2p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); + assert( p->readOnly==0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); @@ -3067,6 +3072,7 @@ case OP_VerifyCookie: { assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); + assert( p->noIO==0 ); pBt = db->aDb[pOp->p1].pBt; if( pBt ){ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); @@ -3162,6 +3168,8 @@ case OP_OpenWrite: { assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 ); assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 ); + assert( p->noIO==0 ); + assert( pOp->opcode==OP_OpenRead || p->readOnly==0 ); if( p->expired ){ rc = SQLITE_ABORT; @@ -4732,6 +4740,7 @@ case OP_Destroy: { /* out2-prerelease */ Vdbe *pVdbe; int iDb; + assert( p->readOnly==0 ); #ifndef SQLITE_OMIT_VIRTUALTABLE iCnt = 0; for(pVdbe=db->pVdbe; pVdbe; pVdbe = pVdbe->pNext){ @@ -4787,6 +4796,7 @@ case OP_Clear: { int nChange; nChange = 0; + assert( p->readOnly==0 ); assert( (p->btreeMask & (((yDbMask)1)<p2))!=0 ); rc = sqlite3BtreeClearTable( db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) @@ -4833,6 +4843,7 @@ case OP_CreateTable: { /* out2-prerelease */ pgno = 0; assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); + assert( p->readOnly==0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ @@ -4980,7 +4991,8 @@ case OP_IntegrityCk: { int nErr; /* Number of errors reported */ char *z; /* Text of the error report */ Mem *pnErr; /* Register keeping track of errors remaining */ - + + assert( p->noIO==0 ); nRoot = pOp->p2; assert( nRoot>0 ); aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(nRoot+1) ); @@ -5486,6 +5498,7 @@ case OP_Checkpoint: { int aRes[3]; /* Results */ Mem *pMem; /* Write results here */ + assert( p->readOnly==0 ); aRes[0] = 0; aRes[1] = aRes[2] = -1; assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE @@ -5535,6 +5548,7 @@ case OP_JournalMode: { /* out2-prerelease */ || eNew==PAGER_JOURNALMODE_QUERY ); assert( pOp->p1>=0 && pOp->p1nDb ); + assert( p->readOnly==0 ); pBt = db->aDb[pOp->p1].pBt; pPager = sqlite3BtreePager(pBt); @@ -5617,6 +5631,7 @@ case OP_JournalMode: { /* out2-prerelease */ ** a transaction. */ case OP_Vacuum: { + assert( p->readOnly==0 ); rc = sqlite3RunVacuum(&p->zErrMsg, db); break; } @@ -5634,6 +5649,7 @@ case OP_IncrVacuum: { /* jump */ assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (((yDbMask)1)<p1))!=0 ); + assert( p->readOnly==0 ); pBt = db->aDb[pOp->p1].pBt; rc = sqlite3BtreeIncrVacuum(pBt); if( rc==SQLITE_DONE ){ @@ -5752,6 +5768,7 @@ case OP_VOpen: { sqlite3_vtab *pVtab; sqlite3_module *pModule; + assert( p->noIO==0 ); pCur = 0; pVtabCursor = 0; pVtab = pOp->p4.pVtab->pVtab; @@ -5968,6 +5985,7 @@ case OP_VRename: { pName = &aMem[pOp->p1]; assert( pVtab->pModule->xRename ); assert( memIsValid(pName) ); + assert( p->readOnly==0 ); REGISTER_TRACE(pOp->p1, pName); assert( pName->flags & MEM_Str ); testcase( pName->enc==SQLITE_UTF8 ); @@ -6019,6 +6037,7 @@ case OP_VUpdate: { assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback || pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace ); + assert( p->readOnly==0 ); pVtab = pOp->p4.pVtab->pVtab; pModule = (sqlite3_module *)pVtab->pModule; nArg = pOp->p2; diff --git a/src/vdbeInt.h b/src/vdbeInt.h index dc42187f06..7b66742063 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -337,7 +337,8 @@ struct Vdbe { bft expired:1; /* True if the VM needs to be recompiled */ bft runOnlyOnce:1; /* Automatically expire on reset */ bft usesStmtJournal:1; /* True if uses a statement journal */ - bft readOnly:1; /* True for read-only statements */ + bft readOnly:1; /* True for statements that do not write */ + bft noIO:1; /* True for SELECTs with no FROM clause */ bft isPrepareV2:1; /* True if prepared with prepare_v2() */ bft doingRerun:1; /* True if rerunning after an auto-reprepare */ int nChange; /* Number of db changes made since last reset */ diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 426b72c5cc..0f3df2e0df 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -396,6 +396,7 @@ static int sqlite3Step(Vdbe *p){ db->activeVdbeCnt++; if( p->readOnly==0 ) db->writeVdbeCnt++; + if( p->noIO ) db->noIOVdbeCnt++; p->pc = 0; } #ifndef SQLITE_OMIT_EXPLAIN diff --git a/src/vdbeaux.c b/src/vdbeaux.c index e32f2db064..3bfaa8ca40 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -403,14 +403,24 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ Op *pOp; int *aLabel = p->aLabel; p->readOnly = 1; + p->noIO = 1; for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){ u8 opcode = pOp->opcode; pOp->opflags = sqlite3OpcodeProperty[opcode]; if( opcode==OP_Function || opcode==OP_AggStep ){ if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5; - }else if( (opcode==OP_Transaction && pOp->p2!=0) || opcode==OP_Vacuum ){ + }else if( opcode==OP_Transaction ){ + if( pOp->p2!=0 ) p->readOnly = 0; + p->noIO = 0; + }else if( opcode==OP_Vacuum + || opcode==OP_JournalMode +#ifndef SQLITE_OMIT_VIRTUALTABLE + || opcode==OP_Checkpoint +#endif + ){ p->readOnly = 0; + p->noIO = 0; #ifndef SQLITE_OMIT_VIRTUALTABLE }else if( opcode==OP_VUpdate ){ if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; @@ -436,7 +446,6 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ } sqlite3DbFree(p->db, p->aLabel); p->aLabel = 0; - *pMaxFuncArgs = nMaxArgs; } @@ -1976,16 +1985,19 @@ static void checkActiveVdbeCnt(sqlite3 *db){ Vdbe *p; int cnt = 0; int nWrite = 0; + int nNoIO = 0; p = db->pVdbe; while( p ){ if( p->magic==VDBE_MAGIC_RUN && p->pc>=0 ){ cnt++; if( p->readOnly==0 ) nWrite++; + if( p->noIO ) nNoIO++; } p = p->pNext; } assert( cnt==db->activeVdbeCnt ); assert( nWrite==db->writeVdbeCnt ); + assert( nNoIO==db->noIOVdbeCnt ); } #else #define checkActiveVdbeCnt(x) @@ -2257,10 +2269,10 @@ int sqlite3VdbeHalt(Vdbe *p){ /* We have successfully halted and closed the VM. Record this fact. */ if( p->pc>=0 ){ db->activeVdbeCnt--; - if( !p->readOnly ){ - db->writeVdbeCnt--; - } + if( !p->readOnly ) db->writeVdbeCnt--; + if( p->noIO ) db->noIOVdbeCnt--; assert( db->activeVdbeCnt>=db->writeVdbeCnt ); + assert( db->activeVdbeCnt>=db->noIOVdbeCnt ); } p->magic = VDBE_MAGIC_HALT; checkActiveVdbeCnt(db); diff --git a/test/capi3d.test b/test/capi3d.test index 746ec20b25..ed3765b051 100644 --- a/test/capi3d.test +++ b/test/capi3d.test @@ -108,6 +108,13 @@ db eval {CREATE TABLE t1(x)} test_is_readonly capi3d-2.3 {INSERT INTO t1 VALUES(5)} 0 test_is_readonly capi3d-2.4 {UPDATE t1 SET x=x+1 WHERE x<0} 0 test_is_readonly capi3d-2.5 {SELECT * FROM t1} 1 +ifcapable wal { + test_is_readonly capi3d-2.6 {PRAGMA journal_mode=WAL} 0 + test_is_readonly capi3d-2.7 {PRAGMA wal_checkpoint} 0 +} +test_is_readonly capi3d-2.8 {PRAGMA application_id=1234} 0 +test_is_readonly capi3d-2.9 {VACUUM} 0 +test_is_readonly capi3d-2.10 {PRAGMA integrity_check} 1 do_test capi3-2.99 { sqlite3_stmt_readonly 0 } 1