Start all transactions and verify all schema cookies near the beginning of

of each vdbe program. (CVS 1543)

FossilOrigin-Name: 1086196460e261718e78512d77e25dde021a117d
This commit is contained in:
drh 2004-06-09 00:48:12 +00:00
parent b3e043463d
commit 80242055e5
11 changed files with 149 additions and 72 deletions

View File

@ -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

View File

@ -1 +1 @@
4dfdea7373f3471d17498da3d6c3aaf926a72d4b
1086196460e261718e78512d77e25dde021a117d

View File

@ -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 <ctype.h>
@ -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 ){
sqlite3VdbeAddOp(v, OP_Halt, 0, 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; iDb<db->nDb; 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);
}
}
/* 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 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
if( iDb!=1 && (iDb>63 || !(pParse->cookieMask & ((u64)1<<iDb))) ){
sqlite3VdbeAddOp(v, OP_Transaction, iDb, 0);
sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, db->aDb[iDb].schema_cookie);
pParse->cookieMask |= ((u64)1<<iDb);
assert( db->aDb[iDb].pBt!=0 || iDb==1 );
assert( iDb<32 );
if( pParse->cookieMask==0 ){
pParse->cookieGoto = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
}
mask = 1<<iDb;
if( (pParse->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<<iDb))) ){
sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, db->aDb[iDb].schema_cookie);
pParse->cookieMask |= ((u64)1<<iDb);
}
sqlite3CodeVerifySchema(pParse, iDb);
pParse->writeMask |= 1<<iDb;
if( setStatement ){
sqlite3VdbeAddOp(v, OP_Statement, iDb, 0);
}

View File

@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.125 2004/05/31 08:55:34 danielk1977 Exp $
** @(#) $Id: parse.y,v 1.126 2004/06/09 00:48:13 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
@ -69,7 +69,7 @@ cmdlist ::= cmdlist ecmd.
cmdlist ::= ecmd.
ecmd ::= explain cmdx SEMI.
ecmd ::= SEMI.
cmdx ::= cmd. { sqlite3Exec(pParse); }
cmdx ::= cmd. { sqlite3FinishCoding(pParse); }
explain ::= EXPLAIN. { sqlite3BeginParse(pParse, 1); }
explain ::= . { sqlite3BeginParse(pParse, 0); }

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file contains code used to implement the PRAGMA command.
**
** $Id: pragma.c,v 1.37 2004/06/07 07:52:18 danielk1977 Exp $
** $Id: pragma.c,v 1.38 2004/06/09 00:48:13 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@ -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)){

View File

@ -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*);

View File

@ -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;
}

View File

@ -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.

View File

@ -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; i<p->nOp; i++){
sqlite3VdbePrintOp(stdout, i, &p->aOp[i]);
}
}
if( sqlite3OsFileExists("vdbe_trace") ){
printf("VDBE Execution Trace:\n");
p->trace = stdout;
}
#endif

View File

@ -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; i<pTabList->nSrc; 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,

View File

@ -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);