From: drh Date: Wed, 9 Jun 2004 00:48:12 +0000 (+0000) Subject: Start all transactions and verify all schema cookies near the beginning of X-Git-Tag: version-3.6.10~4532 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=80242055e53a0e72277cb1180316c3c9fecd6cc1;p=thirdparty%2Fsqlite.git Start all transactions and verify all schema cookies near the beginning of of each vdbe program. (CVS 1543) FossilOrigin-Name: 1086196460e261718e78512d77e25dde021a117d --- diff --git a/manifest b/manifest index dc4d7729cc..47e49d1e42 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\srequire\sa\sRESERVED\slock\swhen\stransitioning\sfrom\sSHARED\sto\sEXCLUSIVE.\s(CVS\s1542) -D 2004-06-08T00:47:47 +C Start\sall\stransactions\sand\sverify\sall\sschema\scookies\snear\sthe\sbeginning\sof\nof\seach\svdbe\sprogram.\s(CVS\s1543) +D 2004-06-09T00:48:12 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -27,7 +27,7 @@ F src/attach.c e76e4590ec5dd389e5646b171881b5243a6ef391 F src/auth.c 5c2f0bea4729c98c2be3b69d6b466fc51448fe79 F src/btree.c edb38affc2e83f4299e49104cfe14e6570d8bd32 F src/btree.h 589427ac13bb544d298cd99726e2572a6fe4bdaa -F src/build.c 83303494ccad0ed1cea24f73c7db1f2669820ccd +F src/build.c f720a2538af6b39a0edc9f62fad3a8c809d455b5 F src/date.c 8e6fa3173386fb29fdef012ee08a853c1e9908b2 F src/delete.c b30f08250c9ed53a25a13c7c04599c1e8753992d F src/encode.c a876af473d1d636faa3dca51c7571f2e007eea37 @@ -49,14 +49,14 @@ F src/os_win.c a13b85a0d4889e3d0b254ed2a61354acddc59fc4 F src/os_win.h 004eec47b1780fcaf07420ddc2072294b698d48c F src/pager.c 3fddd1e5b3e449b19e4f762ab1f1d10786d56d28 F src/pager.h 0c7b5ac45c69e690c45d160d03bdc8fbd2d4657b -F src/parse.y 27c1ce09f9d309be91f9e537df2fb00892990af4 -F src/pragma.c 54b4d67fa81fd38b911aa3325348dcae9ceac5a4 +F src/parse.y 19972fbaa3440f511da36eae1d1fe536fe2c7805 +F src/pragma.c 9328a31c22615758077e8ded1126affe8f5e7fbe F src/printf.c 63b15f1ea9fe3daa066bb7430fd20d4a2d717dc8 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 F src/select.c 0ac0adeb2ae15255ac4399d9ee1b0d25a266a676 F src/shell.c ca519519dcbbc582f6d88f7d0e7583b857fd3469 F src/sqlite.h.in 577974e9a1b85815ccddfb5b858695b62000f595 -F src/sqliteInt.h 845d2a3ffdb9a9050a1b55044d4856227b649b84 +F src/sqliteInt.h 472033b41fe76cf0a9c39dda410cedbadeb61b03 F src/table.c af14284fa36c8d41f6829e3f2819dce07d3e2de2 F src/tclsqlite.c c4d549ad9f5941288247759ead89d9632254fdc3 F src/test1.c 416a37411f2ca7947e6509c5b6ddc6dd86a1204e @@ -65,20 +65,20 @@ F src/test3.c beafd0ccf7b9ae784744be1b1e66ffe8f64c25da F src/test4.c a921a69821fd30209589228e64f94e9f715b6fe2 F src/test5.c 44178ce85c3afd2004ab4eeb5cfd7487116ce366 F src/tokenize.c 183c5d7da11affab5d70d903d33409c8c0ce6c5b -F src/trigger.c 532daca4972bbf1165bdeecf48d9949eee8c24c0 +F src/trigger.c 2c28bf37f21e1ca2fc39cd88db8dbe3ad6ac5419 F src/update.c 259f06e7b22c684b2d3dda54a18185892d6e9573 F src/utf.c c2c8e445bfea724f3502609d6389fe66651f02ab F src/util.c 8b3680271111bcdf5b395916b08b9a6684e0e73d F src/vacuum.c b921eb778842592e1fb48a9d4cef7e861103878f -F src/vdbe.c 392c6b02c525ea12dff403ba4ceb42b0afcb42f5 +F src/vdbe.c 3ffa1effb57f861131f1abc6d4f14db81fad2ade F src/vdbe.h 46f74444a213129bc4b5ce40124dd8ed613b0cde F src/vdbeInt.h ab592f23ed5a1913f9a506bd7b76c5e39377942a F src/vdbeapi.c 4ac95766b0515538037a7aec172ed26142f97cf9 -F src/vdbeaux.c cd1be846336f039442503991fa2aba70f1708554 +F src/vdbeaux.c ab8c99fd5c94ff366f0db3801eb7a1d3f4026e23 F src/vdbemem.c 5d029d83bc60eaf9c45837fcbc0b03348ec95d7a -F src/where.c 444a7c3a8b1eb7bba072e489af628555d21d92a4 +F src/where.c ded00b92dcee77ebe358ff48f5ef05ee8e8ff163 F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242 -F test/attach.test 1635022d7e1d95dc92fe381cc62f9bf25cb29d73 +F test/attach.test aed659e52635662bcd5069599aaca823533edf5a F test/attach2.test 2185dce04ef9ceb7b2d3df7d17fb2c3817028dea F test/attach3.test 8259ab833b5dcdf4acd75d9653f42f703ce2e013 F test/auth.test 95809b8f6a9bec18b94d28cafd03fe27d2f8a9e9 @@ -215,7 +215,7 @@ F www/support.tcl 1801397edd271cc39a2aadd54e701184b5181248 F www/tclsqlite.tcl 19191cf2a1010eaeff74c51d83fd5f5a4d899075 F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 -P 97aa54bb70715934e0af082d51b9b0f6bb847e8e -R b382950b985a7f7c010fa51e63195e87 +P 4dfdea7373f3471d17498da3d6c3aaf926a72d4b +R 6bf109928e5c77863915e3d9ce0daa3e U drh -Z 979bdbef9456a09bba11f45e5a0188cd +Z 588abfea4494baa41bec5420972c8c24 diff --git a/manifest.uuid b/manifest.uuid index fb0060ce47..1d49d7cc41 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4dfdea7373f3471d17498da3d6c3aaf926a72d4b \ No newline at end of file +1086196460e261718e78512d77e25dde021a117d \ No newline at end of file diff --git a/src/build.c b/src/build.c index df6bb336e8..00bf979b07 100644 --- a/src/build.c +++ b/src/build.c @@ -23,7 +23,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.209 2004/06/08 00:02:33 danielk1977 Exp $ +** $Id: build.c,v 1.210 2004/06/09 00:48:12 drh Exp $ */ #include "sqliteInt.h" #include @@ -58,22 +58,44 @@ void sqlite3BeginParse(Parse *pParse, int explainFlag){ /* ** This routine is called after a single SQL statement has been -** parsed and we want to execute the VDBE code to implement -** that statement. Prior action routines should have already -** constructed VDBE code to do the work of the SQL statement. -** This routine just has to execute the VDBE code. +** parsed and a VDBE program to execute that statement has been +** prepared. This routine puts the finishing touches on the +** VDBE program and resets the pParse structure for the next +** parse. ** ** Note that if an error occurred, it might be the case that ** no VDBE code was generated. */ -void sqlite3Exec(Parse *pParse){ - sqlite *db = pParse->db; - Vdbe *v = pParse->pVdbe; +void sqlite3FinishCoding(Parse *pParse){ + sqlite *db; + Vdbe *v; - if( v==0 && (v = sqlite3GetVdbe(pParse))!=0 ){ + if( sqlite3_malloc_failed ) return; + + /* Begin by generating some termination code at the end of the + ** vdbe program + */ + db = pParse->db; + v = sqlite3GetVdbe(pParse); + if( v ){ sqlite3VdbeAddOp(v, OP_Halt, 0, 0); + if( pParse->cookieMask!=0 ){ + u32 mask; + int iDb; + sqlite3VdbeChangeP2(v, pParse->cookieGoto, sqlite3VdbeCurrentAddr(v)); + for(iDb=0, mask=1; iDbnDb; mask<<=1, iDb++){ + if( (mask & pParse->cookieMask)==0 ) continue; + sqlite3VdbeAddOp(v, OP_Transaction, iDb, (mask & pParse->writeMask)!=0); + if( iDb!=1 ){ + sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, pParse->cookieValue[iDb]); + } + } + sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->cookieGoto+1); + } } - if( sqlite3_malloc_failed ) return; + + /* Get the VDBE program ready for execution + */ if( v && pParse->nErr==0 ){ FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0; sqlite3VdbeTrace(v, trace); @@ -88,6 +110,7 @@ void sqlite3Exec(Parse *pParse){ pParse->nSet = 0; pParse->nAgg = 0; pParse->nVar = 0; + pParse->cookieMask = 0; } /* @@ -2249,19 +2272,41 @@ void sqlite3RollbackTransaction(Parse *pParse){ } /* -** Generate VDBE code that will verify the schema cookie for all -** named database files. +** Generate VDBE code that will verify the schema cookie and start +** a read-transaction for all named database files. +** +** It is important that all schema cookies be verified and all +** read transactions be started before anything else happens in +** the VDBE program. But this routine can be called after much other +** code has been generated. So here is what we do: +** +** The first time this routine is called, we code an OP_Gosub that +** will jump to a subroutine at the end of the program. Then we +** record every database that needs its schema verified in the +** pParse->cookieMask field. Later, after all other code has been +** generated, the subroutine that does the cookie verifications and +** starts the transactions will be coded and the OP_Gosub P2 value +** will be made to point to that subroutine. The generation of the +** cookie verification subroutine code happens in sqlite3FinishCoding(). */ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ - sqlite *db = pParse->db; - Vdbe *v = sqlite3GetVdbe(pParse); - if( v==0 ) return; + sqlite *db; + Vdbe *v; + int mask; + + v = sqlite3GetVdbe(pParse); + if( v==0 ) return; /* This only happens if there was a prior error */ + db = pParse->db; assert( iDb>=0 && iDbnDb ); - assert( db->aDb[iDb].pBt!=0 ); - if( iDb!=1 && (iDb>63 || !(pParse->cookieMask & ((u64)1<aDb[iDb].schema_cookie); - pParse->cookieMask |= ((u64)1<aDb[iDb].pBt!=0 || iDb==1 ); + assert( iDb<32 ); + if( pParse->cookieMask==0 ){ + pParse->cookieGoto = sqlite3VdbeAddOp(v, OP_Goto, 0, 0); + } + mask = 1<cookieMask & mask)==0 ){ + pParse->cookieMask |= mask; + pParse->cookieValue[iDb] = db->aDb[iDb].schema_cookie; } } @@ -2287,11 +2332,8 @@ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ sqlite *db = pParse->db; Vdbe *v = sqlite3GetVdbe(pParse); if( v==0 ) return; - sqlite3VdbeAddOp(v, OP_Transaction, iDb, 1); - if( (iDb>63 || !(pParse->cookieMask & ((u64)1<aDb[iDb].schema_cookie); - pParse->cookieMask |= ((u64)1<writeMask |= 1< @@ -599,6 +599,8 @@ void sqlite3Pragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ HashElem *x; int cnt = 0; + sqlite3CodeVerifySchema(pParse, i); + /* Do an integrity check of the B-Tree */ for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index bc73f021d2..c738b4363e 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.271 2004/06/07 07:52:18 danielk1977 Exp $ +** @(#) $Id: sqliteInt.h,v 1.272 2004/06/09 00:48:13 drh Exp $ */ #include "config.h" #include "sqlite3.h" @@ -58,8 +58,9 @@ /* ** The maximum number of attached databases. This must be at least 2 ** in order to support the main database file (0) and the file used to -** hold temporary tables (1). And it must be less than 256 because -** an unsigned character is used to stored the database index. +** hold temporary tables (1). And it must be less than 32 because +** we use a bitmask of databases with a u32 in places (for example +** the Parse.cookieMask field). */ #define MAX_ATTACHED 10 @@ -1005,7 +1006,10 @@ struct Parse { const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ TriggerStack *trigStack; /* Trigger actions being coded */ - u64 cookieMask; /* Bitmask of schema verified databases */ + u32 cookieMask; /* Bitmask of schema verified databases */ + int cookieValue[MAX_ATTACHED+2]; /* Values of cookies to verify */ + int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ + u32 writeMask; /* Start a write transaction on these databases */ }; /* @@ -1202,7 +1206,7 @@ void sqlite3ErrorMsg(Parse*, const char*, ...); void sqlite3Dequote(char*); int sqlite3KeywordCode(const char*, int); int sqlite3RunParser(Parse*, const char*, char **); -void sqlite3Exec(Parse*); +void sqlite3FinishCoding(Parse*); Expr *sqlite3Expr(int, Expr*, Expr*, Token*); void sqlite3ExprSpan(Expr*,Token*,Token*); Expr *sqlite3ExprFunction(ExprList*, Token*); diff --git a/src/trigger.c b/src/trigger.c index b40ae7d2fe..38a573d9a1 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -710,7 +710,6 @@ int sqlite3CodeRowTrigger( ){ Trigger * pTrigger; TriggerStack * pTriggerStack; - u64 cookieMask = pParse->cookieMask; assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE); assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER ); @@ -781,10 +780,5 @@ int sqlite3CodeRowTrigger( } pTrigger = pTrigger->pNext; } - - pParse->cookieMask = cookieMask; return 0; } - - - diff --git a/src/vdbe.c b/src/vdbe.c index 848d7cccf8..e82a9852d2 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.359 2004/06/06 09:44:05 danielk1977 Exp $ +** $Id: vdbe.c,v 1.360 2004/06/09 00:48:14 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -2302,11 +2302,11 @@ case OP_AutoCommit: { ** started. Index 0 is the main database file and index 1 is the ** file used for temporary tables. ** -** If P2 is non-zero, then a write-transaction is started. A write lock is +** If P2 is non-zero, then a write-transaction is started. A RESERVED lock is ** obtained on the database file when a write-transaction is started. No -** other process can read or write the file while the transaction is -** underway. Starting a transaction also creates a rollback journal. A -** transaction must be started before any changes can be made to the +** other process can start another write transaction while this transaction is +** underway. Starting a write transaction also creates a rollback journal. A +** write transaction must be started before any changes can be made to the ** database. ** ** If P2 is zero, then a read-lock is obtained on the database file. diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 3682320cd1..a168e49084 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -654,7 +654,15 @@ void sqlite3VdbeMakeReady( sqlite3HashInit(&p->agg.hash, SQLITE_HASH_BINARY, 0); p->agg.pSearch = 0; #ifdef MEMORY_DEBUG + if( sqlite3OsFileExists("vdbe_explain") ){ + int i; + printf("VDBE Program Listing:\n"); + for(i=0; inOp; i++){ + sqlite3VdbePrintOp(stdout, i, &p->aOp[i]); + } + } if( sqlite3OsFileExists("vdbe_trace") ){ + printf("VDBE Execution Trace:\n"); p->trace = stdout; } #endif diff --git a/src/where.c b/src/where.c index 166e29927d..50733486e7 100644 --- a/src/where.c +++ b/src/where.c @@ -12,7 +12,7 @@ ** This module contains C code that generates VDBE code used to process ** the WHERE clause of SQL statements. ** -** $Id: where.c,v 1.101 2004/05/29 11:24:50 danielk1977 Exp $ +** $Id: where.c,v 1.102 2004/06/09 00:48:15 drh Exp $ */ #include "sqliteInt.h" @@ -671,6 +671,7 @@ WhereInfo *sqlite3WhereBegin( /* Open all tables in the pTabList and all indices used by those tables. */ + sqlite3CodeVerifySchema(pParse, 1); /* Inserts the cookie verifier Goto */ for(i=0; inSrc; i++){ Table *pTab; Index *pIx; @@ -680,7 +681,9 @@ WhereInfo *sqlite3WhereBegin( sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqlite3VdbeAddOp(v, OP_OpenRead, pTabList->a[i].iCursor, pTab->tnum); sqlite3VdbeAddOp(v, OP_SetNumColumns, pTabList->a[i].iCursor, pTab->nCol); - sqlite3CodeVerifySchema(pParse, pTab->iDb); + if( pTab->tnum>1 ){ + sqlite3CodeVerifySchema(pParse, pTab->iDb); + } if( (pIx = pWInfo->a[i].pIdx)!=0 ){ sqlite3VdbeAddOp(v, OP_Integer, pIx->iDb, 0); sqlite3VdbeOp3(v, OP_OpenRead, pWInfo->a[i].iCur, pIx->tnum, diff --git a/test/attach.test b/test/attach.test index af3f529d6a..1c165cf9c2 100644 --- a/test/attach.test +++ b/test/attach.test @@ -12,7 +12,7 @@ # focus of this script is testing the ATTACH and DETACH commands # and related functionality. # -# $Id: attach.test,v 1.22 2004/06/07 01:52:15 drh Exp $ +# $Id: attach.test,v 1.23 2004/06/09 00:48:15 drh Exp $ # set testdir [file dirname $argv0] @@ -314,7 +314,8 @@ do_test attach-3.3 { } } {0 {21 x 22 y}} -# Even though main has a transaction, test2.db should not be locked. +# Even though 'db' has started a transaction, it should not yet have +# a lock on test2.db so 'db2' should be readable. do_test attach-3.4 { execsql BEGIN catchsql { @@ -322,7 +323,8 @@ do_test attach-3.4 { } db2; } {0 {21 x 22 y}} -# Reading from db2 should not lock test2.db +# Reading from test2.db from db within a transaction should not +# prevent test2.db from being read by db2. do_test attach-3.5 { execsql {SELECT * FROM t2} catchsql { @@ -330,7 +332,8 @@ do_test attach-3.5 { } db2; } {0 {21 x 22 y}} -# Making a change to db2 causes test2.ddb to become locked. +# Making a change to test2.db through db causes test2.db to get +# a reserved lock. It should still be accessible through db2. do_test attach-3.6 { execsql { UPDATE t2 SET x=x+1 WHERE x=50; @@ -338,30 +341,42 @@ do_test attach-3.6 { catchsql { SELECT * FROM t2; } db2; -} {1 {database is locked}} +} {0 {21 x 22 y}} do_test attach-3.7 { execsql ROLLBACK execsql {SELECT * FROM t2} db2 } {21 x 22 y} + +# Start transactions on both db and db2. Once again, just because +# we make a change to test2.db using db2, only a RESERVED lock is +# obtained, so test2.db should still be readable using db. +# do_test attach-3.8 { execsql BEGIN execsql BEGIN db2 execsql {UPDATE t2 SET x=0 WHERE 0} db2 catchsql {SELECT * FROM t2} -} {1 {database is locked}} +} {0 {21 x 22 y}} + +# It is also still accessible from db2. do_test attach-3.9 { catchsql {SELECT * FROM t2} db2 } {0 {21 x 22 y}} + do_test attach-3.10 { execsql {SELECT * FROM t1} } {1 2 3 4} + do_test attach-3.11 { catchsql {UPDATE t1 SET a=a+1} } {0 {}} do_test attach-3.12 { execsql {SELECT * FROM t1} } {2 2 4 4} + +# db2 has a RESERVED lock on test2.db, so db cannot write to any tables +# in test2.db. do_test attach-3.13 { catchsql {UPDATE t2 SET x=x+1 WHERE x=50} } {1 {database is locked}} @@ -370,18 +385,18 @@ do_test attach-3.13 { # for a locked database. execsql {ROLLBACK} +# db is able to reread its schema because db2 still only holds a +# reserved lock. do_test attach-3.14 { - # Unable to reinitialize the schema tables because the aux database - # is still locked. catchsql {SELECT * FROM t1} -} {1 {database is locked}} +} {0 {1 2 3 4}} do_test attach-3.15 { execsql COMMIT db2 execsql {SELECT * FROM t1} } {1 2 3 4} #set btree_trace 1 -#puts stderr "###################"; flush stderr + # Ticket #323 do_test attach-4.1 { execsql {DETACH db2} @@ -442,6 +457,14 @@ do_test attach-4.7 { SELECT * FROM main.t4; } } {main.11} + +# This one is tricky. On the UNION ALL select, we have to make sure +# the schema for both main and db2 is valid before starting to execute +# the first query of the UNION ALL. If we wait to test the validity of +# the schema for main until after the first query has run, that test will +# fail and the query will abort but we will have already output some +# results. When the query is retried, the results will be repeated. +# do_test attach-4.8 { execsql { ATTACH DATABASE 'test2.db' AS db2; @@ -449,6 +472,7 @@ do_test attach-4.8 { SELECT * FROM db2.t4 UNION ALL SELECT * FROM main.t4; } } {db2.6 db2.13 main.11} + do_test attach-4.9 { execsql { INSERT INTO main.t3 VALUES(15,16);