diff options
author | danielk1977 <danielk1977@noemail.net> | 2004-05-31 08:26:49 +0000 |
---|---|---|
committer | danielk1977 <danielk1977@noemail.net> | 2004-05-31 08:26:49 +0000 |
commit | 1d850a72c265ad9368b322ca39125c9b0c3d8509 (patch) | |
tree | 3ae42a002f87796ecea58f7094dea2096fa4439a /src | |
parent | a19b775db9b2f12263e54cf0f64162ced526fff5 (diff) | |
download | sqlite-1d850a72c265ad9368b322ca39125c9b0c3d8509.tar.gz sqlite-1d850a72c265ad9368b322ca39125c9b0c3d8509.zip |
Replace OP_Begin, OP_Commit and OP_Rollback with OP_AutoCommit. (CVS 1500)
FossilOrigin-Name: b8ed812c92f2dbb4431d45aeb41646ceb53e0cbc
Diffstat (limited to 'src')
-rw-r--r-- | src/btree.c | 16 | ||||
-rw-r--r-- | src/btree.h | 4 | ||||
-rw-r--r-- | src/build.c | 94 | ||||
-rw-r--r-- | src/main.c | 3 | ||||
-rw-r--r-- | src/pager.c | 6 | ||||
-rw-r--r-- | src/pragma.c | 4 | ||||
-rw-r--r-- | src/sqliteInt.h | 5 | ||||
-rw-r--r-- | src/test3.c | 86 | ||||
-rw-r--r-- | src/trigger.c | 10 | ||||
-rw-r--r-- | src/vacuum.c | 28 | ||||
-rw-r--r-- | src/vdbe.c | 115 | ||||
-rw-r--r-- | src/vdbeInt.h | 1 | ||||
-rw-r--r-- | src/vdbeapi.c | 6 | ||||
-rw-r--r-- | src/vdbeaux.c | 111 |
14 files changed, 297 insertions, 192 deletions
diff --git a/src/btree.c b/src/btree.c index 8057be79b..86b98d671 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.152 2004/05/30 20:46:09 drh Exp $ +** $Id: btree.c,v 1.153 2004/05/31 08:26:49 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -4183,3 +4183,17 @@ int sqlite3BtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){ } return rc; } + +/* +** Return non-zero if a transaction is active. +*/ +int sqlite3BtreeIsInTrans(Btree *pBt){ + return (pBt && pBt->inTrans); +} + +/* +** Return non-zero if a statement transaction is active. +*/ +int sqlite3BtreeIsInStmt(Btree *pBt){ + return (pBt && pBt->inStmt); +} diff --git a/src/btree.h b/src/btree.h index 639e55bcd..c6c4f08bc 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.49 2004/05/30 20:46:09 drh Exp $ +** @(#) $Id: btree.h,v 1.50 2004/05/31 08:26:49 danielk1977 Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -48,6 +48,8 @@ int sqlite3BtreeBeginStmt(Btree*); int sqlite3BtreeCommitStmt(Btree*); int sqlite3BtreeRollbackStmt(Btree*); int sqlite3BtreeCreateTable(Btree*, int*, int flags); +int sqlite3BtreeIsInTrans(Btree*); +int sqlite3BtreeIsInStmt(Btree*); const char *sqlite3BtreeGetFilename(Btree *); int sqlite3BtreeCopyFile(Btree *, Btree *); diff --git a/src/build.c b/src/build.c index 5699c5d3d..6aeb8cbd8 100644 --- a/src/build.c +++ b/src/build.c @@ -23,7 +23,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.200 2004/05/29 11:24:50 danielk1977 Exp $ +** $Id: build.c,v 1.201 2004/05/31 08:26:49 danielk1977 Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -920,6 +920,15 @@ char sqlite3AffinityType(const char *zType, int nType){ ** 1 chance in 2^32. So we're safe enough. */ void sqlite3ChangeCookie(sqlite *db, Vdbe *v, int iDb){ + unsigned char r; + int *pSchemaCookie = &(db->aDb[iDb].schema_cookie); + + sqlite3Randomness(1, &r); + *pSchemaCookie = *pSchemaCookie + r + 1; + db->flags |= SQLITE_InternChanges; + sqlite3VdbeAddOp(v, OP_Integer, *pSchemaCookie, 0); + sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 0); +/* if( db->next_cookie==db->aDb[0].schema_cookie ){ unsigned char r; sqlite3Randomness(1, &r); @@ -928,6 +937,7 @@ void sqlite3ChangeCookie(sqlite *db, Vdbe *v, int iDb){ sqlite3VdbeAddOp(v, OP_Integer, db->next_cookie, 0); sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 0); } +*/ } /* @@ -1104,7 +1114,7 @@ void sqlite3EndTable(Parse *pParse, Token *pEnd, Select *pSelect){ } sqlite3VdbeOp3(v, OP_MakeRecord, 5, 0, "tttit", P3_STATIC); sqlite3VdbeAddOp(v, OP_PutIntKey, 0, 0); - if( !p->iDb ){ + if( p->iDb!=1 ){ sqlite3ChangeCookie(db, v, p->iDb); } sqlite3VdbeAddOp(v, OP_Close, 0, 0); @@ -1830,7 +1840,6 @@ void sqlite3CreateIndex( int n; Vdbe *v; int lbl1, lbl2; - int i; v = sqlite3GetVdbe(pParse); if( v==0 ) goto exit_create_index; @@ -2165,19 +2174,17 @@ void sqlite3SrcListDelete(SrcList *pList){ */ void sqlite3BeginTransaction(Parse *pParse, int onError){ sqlite *db; + Vdbe *v; if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; if( pParse->nErr || sqlite3_malloc_failed ) return; if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ) return; - if( db->flags & SQLITE_InTrans ){ - sqlite3ErrorMsg(pParse, "cannot start a transaction within a transaction"); - return; - } - sqlite3BeginWriteOperation(pParse, 0, 0); - if( !pParse->explain ){ - db->flags |= SQLITE_InTrans; - db->onError = onError; - } + + v = sqlite3GetVdbe(pParse); + if( !v ) return; + sqlite3VdbeAddOp(v, OP_AutoCommit, 0, 0); + + /* FIX ME: Need to deal with onError */ } /* @@ -2185,20 +2192,15 @@ void sqlite3BeginTransaction(Parse *pParse, int onError){ */ void sqlite3CommitTransaction(Parse *pParse){ sqlite *db; + Vdbe *v; if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; if( pParse->nErr || sqlite3_malloc_failed ) return; if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ) return; - if( (db->flags & SQLITE_InTrans)==0 ){ - sqlite3ErrorMsg(pParse, "cannot commit - no transaction is active"); - return; - } - if( !pParse->explain ){ - db->flags &= ~SQLITE_InTrans; - } - sqlite3EndWriteOperation(pParse); - if( !pParse->explain ){ - db->onError = OE_Default; + + v = sqlite3GetVdbe(pParse); + if( v ){ + sqlite3VdbeAddOp(v, OP_AutoCommit, 1, 0); } } @@ -2212,17 +2214,10 @@ void sqlite3RollbackTransaction(Parse *pParse){ if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; if( pParse->nErr || sqlite3_malloc_failed ) return; if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ) return; - if( (db->flags & SQLITE_InTrans)==0 ){ - sqlite3ErrorMsg(pParse, "cannot rollback - no transaction is active"); - return; - } + v = sqlite3GetVdbe(pParse); if( v ){ - sqlite3VdbeAddOp(v, OP_Rollback, 0, 0); - } - if( !pParse->explain ){ - db->flags &= ~SQLITE_InTrans; - db->onError = OE_Default; + sqlite3VdbeAddOp(v, OP_AutoCommit, 1, 1); } } @@ -2235,9 +2230,9 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ Vdbe *v = sqlite3GetVdbe(pParse); assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 ); - if( iDb!=1 && !DbHasProperty(db, iDb, DB_Cookie) ){ + if( iDb!=1 && (iDb>63 || !(pParse->cookieMask & ((u64)1<<iDb))) ){ sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, db->aDb[iDb].schema_cookie); - DbSetProperty(db, iDb, DB_Cookie); + pParse->cookieMask |= ((u64)1<<iDb); } } @@ -2260,21 +2255,15 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ ** specified auxiliary database and the temp database are made writable. */ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ - Vdbe *v; - sqlite *db = pParse->db; - if( DbHasProperty(db, iDb, DB_Locked) ) return; - v = sqlite3GetVdbe(pParse); + Vdbe *v = sqlite3GetVdbe(pParse); if( v==0 ) return; - if( !db->aDb[iDb].inTrans ){ - sqlite3VdbeAddOp(v, OP_Transaction, iDb, 0); - DbSetProperty(db, iDb, DB_Locked); - sqlite3CodeVerifySchema(pParse, iDb); - if( iDb!=1 ){ - sqlite3BeginWriteOperation(pParse, setStatement, 1); - } - }else if( setStatement ){ + sqlite3VdbeAddOp(v, OP_Transaction, iDb, 0); + sqlite3CodeVerifySchema(pParse, iDb); + if( setStatement ){ sqlite3VdbeAddOp(v, OP_Statement, iDb, 0); - DbSetProperty(db, iDb, DB_Locked); + } + if( iDb!=1 ){ + sqlite3BeginWriteOperation(pParse, setStatement, 1); } } @@ -2289,15 +2278,6 @@ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ ** call to sqlite3EndWriteOperation() at the conclusion of the statement. */ void sqlite3EndWriteOperation(Parse *pParse){ - Vdbe *v; - sqlite *db = pParse->db; - if( pParse->trigStack ) return; /* if this is in a trigger */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) return; - if( db->flags & SQLITE_InTrans ){ - /* A BEGIN has executed. Do not commit until we see an explicit - ** COMMIT statement. */ - }else{ - sqlite3VdbeAddOp(v, OP_Commit, 0, 0); - } + /* Delete me! */ + return; } diff --git a/src/main.c b/src/main.c index b3fbebc70..af8e74b80 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.196 2004/05/29 10:23:19 danielk1977 Exp $ +** $Id: main.c,v 1.197 2004/05/31 08:26:49 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -1020,6 +1020,7 @@ static int openDatabase( db->nDb = 2; db->aDb = db->aDbStatic; db->enc = def_enc; + db->autoCommit = 1; /* db->flags |= SQLITE_ShortColNames; */ sqlite3HashInit(&db->aFunc, SQLITE_HASH_STRING, 0); sqlite3HashInit(&db->aCollSeq, SQLITE_HASH_STRING, 0); diff --git a/src/pager.c b/src/pager.c index b5315e8f5..d2b84b97b 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.108 2004/05/14 01:58:13 drh Exp $ +** @(#) $Id: pager.c,v 1.109 2004/05/31 08:26:49 danielk1977 Exp $ */ #include "os.h" /* Must be first to enable large file support */ #include "sqliteInt.h" @@ -865,7 +865,7 @@ end_stmt_playback: void sqlite3pager_set_cachesize(Pager *pPager, int mxPage){ if( mxPage>=0 ){ pPager->noSync = pPager->tempFile; - if( pPager->noSync==0 ) pPager->needSync = 0; + if( pPager->noSync ) pPager->needSync = 0; }else{ pPager->noSync = 1; mxPage = -mxPage; @@ -904,7 +904,7 @@ void sqlite3pager_set_cachesize(Pager *pPager, int mxPage){ void sqlite3pager_set_safety_level(Pager *pPager, int level){ pPager->noSync = level==1 || pPager->tempFile; pPager->fullSync = level==3 && !pPager->tempFile; - if( pPager->noSync==0 ) pPager->needSync = 0; + if( pPager->noSync ) pPager->needSync = 0; } /* diff --git a/src/pragma.c b/src/pragma.c index 1c50d37aa..d80005b41 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to implement the PRAGMA command. ** -** $Id: pragma.c,v 1.34 2004/05/29 11:24:50 danielk1977 Exp $ +** $Id: pragma.c,v 1.35 2004/05/31 08:26:49 danielk1977 Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -93,7 +93,7 @@ static int changeTempStorage(Parse *pParse, const char *zStorageType){ sqlite *db = pParse->db; if( db->temp_store==ts ) return SQLITE_OK; if( db->aDb[1].pBt!=0 ){ - if( db->flags & SQLITE_InTrans ){ + if( !db->autoCommit ){ sqlite3ErrorMsg(pParse, "temporary storage cannot be changed " "from within a transaction"); return SQLITE_ERROR; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index f1d13b52f..9abf2d301 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.261 2004/05/29 02:37:19 danielk1977 Exp $ +** @(#) $Id: sqliteInt.h,v 1.262 2004/05/31 08:26:49 danielk1977 Exp $ */ #include "config.h" #include "sqlite.h" @@ -389,6 +389,7 @@ struct sqlite { u8 busy; /* TRUE if currently initializing */ } init; struct Vdbe *pVdbe; /* List of active virtual machines */ + int activeVdbeCnt; /* Number of vdbes currently executing */ void (*xTrace)(void*,const char*); /* Trace function */ void *pTraceArg; /* Argument to the trace function */ #ifndef SQLITE_OMIT_AUTHORIZATION @@ -406,6 +407,7 @@ struct sqlite { char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ void *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ u8 enc; /* Text encoding for this database. */ + u8 autoCommit; /* The auto-commit flag. */ }; /* @@ -989,6 +991,7 @@ 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 */ }; /* diff --git a/src/test3.c b/src/test3.c index a402b3146..5c03c7a07 100644 --- a/src/test3.c +++ b/src/test3.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test3.c,v 1.39 2004/05/30 20:46:09 drh Exp $ +** $Id: test3.c,v 1.40 2004/05/31 08:26:49 danielk1977 Exp $ */ #include "sqliteInt.h" #include "pager.h" @@ -192,6 +192,87 @@ static int btree_commit( } /* +** Usage: btree_begin_statement ID +** +** Start a new statement transaction +*/ +static int btree_begin_statement( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + const char **argv /* Text of each argument */ +){ + Btree *pBt; + int rc; + if( argc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ID\"", 0); + return TCL_ERROR; + } + if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR; + rc = sqlite3BtreeBeginStmt(pBt); + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp, errorName(rc), 0); + return TCL_ERROR; + } + return TCL_OK; +} + +/* +** Usage: btree_rollback_statement ID +** +** Rollback changes +*/ +static int btree_rollback_statement( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + const char **argv /* Text of each argument */ +){ + Btree *pBt; + int rc; + if( argc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ID\"", 0); + return TCL_ERROR; + } + if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR; + rc = sqlite3BtreeRollbackStmt(pBt); + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp, errorName(rc), 0); + return TCL_ERROR; + } + return TCL_OK; +} + +/* +** Usage: btree_commit_statement ID +** +** Commit all changes +*/ +static int btree_commit_statement( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + const char **argv /* Text of each argument */ +){ + Btree *pBt; + int rc; + if( argc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ID\"", 0); + return TCL_ERROR; + } + if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR; + rc = sqlite3BtreeCommitStmt(pBt); + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp, errorName(rc), 0); + return TCL_ERROR; + } + return TCL_OK; +} + +/* ** Usage: btree_create_table ID FLAGS ** ** Create a new table in the database @@ -1257,6 +1338,9 @@ int Sqlitetest3_Init(Tcl_Interp *interp){ { "btree_integrity_check", (Tcl_CmdProc*)btree_integrity_check }, { "btree_breakpoint", (Tcl_CmdProc*)btree_breakpoint }, { "btree_varint_test", (Tcl_CmdProc*)btree_varint_test }, + { "btree_begin_statement", (Tcl_CmdProc*)btree_begin_statement }, + { "btree_commit_statement", (Tcl_CmdProc*)btree_commit_statement }, + { "btree_rollback_statement", (Tcl_CmdProc*)btree_rollback_statement }, }; int i; diff --git a/src/trigger.c b/src/trigger.c index bb4499847..20f71b57d 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -231,8 +231,8 @@ void sqlite3FinishTrigger( sqlite3VdbeChangeP3(v, addr+2, nt->name, 0); sqlite3VdbeChangeP3(v, addr+3, nt->table, 0); sqlite3VdbeChangeP3(v, addr+6, pAll->z, pAll->n); - if( nt->iDb==0 ){ - sqlite3ChangeCookie(db, v, 0); + if( nt->iDb!=0 ){ + sqlite3ChangeCookie(db, v, nt->iDb); } sqlite3VdbeAddOp(v, OP_Close, 0, 0); sqlite3EndWriteOperation(pParse); @@ -488,8 +488,8 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){ sqlite3OpenMasterTable(v, pTrigger->iDb); base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); sqlite3VdbeChangeP3(v, base+1, pTrigger->name, 0); - if( pTrigger->iDb==0 ){ - sqlite3ChangeCookie(db, v, 0); + if( pTrigger->iDb!=1 ){ + sqlite3ChangeCookie(db, v, pTrigger->iDb); } sqlite3VdbeAddOp(v, OP_Close, 0, 0); sqlite3EndWriteOperation(pParse); @@ -711,6 +711,7 @@ 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 ); @@ -782,6 +783,7 @@ int sqlite3CodeRowTrigger( pTrigger = pTrigger->pNext; } + pParse->cookieMask = cookieMask; return 0; } diff --git a/src/vacuum.c b/src/vacuum.c index be47ee938..ff0a5b4fb 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -14,7 +14,7 @@ ** Most of the code in this file may be omitted by defining the ** SQLITE_OMIT_VACUUM macro. ** -** $Id: vacuum.c,v 1.18 2004/05/29 10:43:07 danielk1977 Exp $ +** $Id: vacuum.c,v 1.19 2004/05/31 08:26:49 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -96,11 +96,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite *db){ int nFilename; /* number of characters in zFilename[] */ char *zTemp = 0; /* a temporary file in same directory as zFilename */ int i; /* Loop counter */ + Btree *pTemp; char *zSql = 0; sqlite3_stmt *pStmt = 0; - if( db->flags & SQLITE_InTrans ){ + if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction", (char*)0); rc = SQLITE_ERROR; @@ -189,20 +190,15 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite *db){ ** opened for writing. This way, the SQL transaction used to create the ** temporary database never needs to be committed. */ - - /* FIX ME: The above will be the case shortly. But for now, a transaction - ** will have been started on the main database file by the 'BEGIN'. - */ -/* - rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt); - if( rc!=SQLITE_OK ) goto end_of_vacuum; -*/ - - if( db->aDb[db->nDb-1].inTrans ){ - Btree *pTemp = db->aDb[db->nDb-1].pBt; + pTemp = db->aDb[db->nDb-1].pBt; + if( sqlite3BtreeIsInTrans(pTemp) ){ Btree *pMain = db->aDb[0].pBt; u32 meta; + assert( 0==sqlite3BtreeIsInTrans(pMain) ); + rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt); + if( rc!=SQLITE_OK ) goto end_of_vacuum; + /* Copy Btree meta values 3 and 4. These correspond to SQL layer meta ** values 2 and 3, the default values of a couple of pragmas. */ @@ -216,13 +212,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite *db){ if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCopyFile(pMain, pTemp); - - /* FIX ME: Remove the main btree from the transaction so that it is not - ** rolled back. This won't be required once the new 'auto-commit' - ** model is in place. - */ rc = sqlite3BtreeCommit(pMain); - db->aDb[0].inTrans = 0; } end_of_vacuum: diff --git a/src/vdbe.c b/src/vdbe.c index 5c20d830b..787b0eeff 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.350 2004/05/30 21:14:59 drh Exp $ +** $Id: vdbe.c,v 1.351 2004/05/31 08:26:49 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -2196,9 +2196,40 @@ case OP_MakeRecord: { */ case OP_Statement: { int i = pOp->p1; - if( i>=0 && i<db->nDb && db->aDb[i].pBt && db->aDb[i].inTrans==1 ){ - rc = sqlite3BtreeBeginStmt(db->aDb[i].pBt); - if( rc==SQLITE_OK ) db->aDb[i].inTrans = 2; + Btree *pBt; + if( i>=0 && i<db->nDb && (pBt = db->aDb[i].pBt) && !(db->autoCommit) ){ + assert( sqlite3BtreeIsInTrans(pBt) ); + if( !sqlite3BtreeIsInStmt(pBt) ){ + rc = sqlite3BtreeBeginStmt(pBt); + } + } + break; +} + +/* Opcode: AutoCommit P1 P2 * +** +** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll +** back any currently active btree transactions. +*/ +case OP_AutoCommit: { + u8 i = pOp->p1; + u8 rollback = pOp->p2; + + assert( i==1 || i==0 ); + assert( i==1 || rollback==0 ); + + if( i!=db->autoCommit ){ + db->autoCommit = i; + if( pOp->p2 ){ + sqlite3RollbackAll(db); + } + }else{ + sqlite3SetString(&p->zErrMsg, + (!i)?"cannot start a transaction within a transaction":( + (rollback)?"cannot rollback - no transaction is active": + "cannot commit - no transaction is active"), 0); + + rc = SQLITE_ERROR; } break; } @@ -2222,15 +2253,18 @@ case OP_Statement: { case OP_Transaction: { int busy = 1; int i = pOp->p1; + Btree *pBt; + assert( i>=0 && i<db->nDb ); - if( db->aDb[i].inTrans ) break; - while( db->aDb[i].pBt!=0 && busy ){ + pBt = db->aDb[i].pBt; + + if( sqlite3BtreeIsInTrans(pBt) ) break; + while( pBt && busy /* && !sqlite3BtreeIsInTrans(pBt) */ ){ rc = sqlite3BtreeBeginTrans(db->aDb[i].pBt); switch( rc ){ case SQLITE_BUSY: { if( db->xBusyCallback==0 ){ p->pc = pc; - p->undoTransOnError = 1; p->rc = SQLITE_BUSY; p->pTos = pTos; return SQLITE_BUSY; @@ -2245,7 +2279,6 @@ case OP_Transaction: { /* Fall thru into the next case */ } case SQLITE_OK: { - p->inTempTrans = 0; busy = 0; break; } @@ -2254,58 +2287,6 @@ case OP_Transaction: { } } } - db->aDb[i].inTrans = 1; - p->undoTransOnError = 1; - break; -} - -/* Opcode: Commit * * * -** -** Cause all modifications to the database that have been made since the -** last Transaction to actually take effect. No additional modifications -** are allowed until another transaction is started. The Commit instruction -** deletes the journal file and releases the write lock on the database. -** A read lock continues to be held if there are still cursors open. -*/ -case OP_Commit: { - int i; - if( db->xCommitCallback!=0 ){ - if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - if( db->xCommitCallback(db->pCommitArg)!=0 ){ - rc = SQLITE_CONSTRAINT; - } - if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; - } - for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ - if( db->aDb[i].inTrans ){ - rc = sqlite3BtreeCommit(db->aDb[i].pBt); - db->aDb[i].inTrans = 0; - } - } - if( rc==SQLITE_OK ){ - sqlite3CommitInternalChanges(db); - }else{ - sqlite3RollbackAll(db); - } - break; -} - -/* Opcode: Rollback P1 * * -** -** Cause all modifications to the database that have been made since the -** last Transaction to be undone. The database is restored to its state -** before the Transaction opcode was executed. No additional modifications -** are allowed until another transaction is started. -** -** P1 is the index of the database file that is committed. An index of 0 -** is used for the main database and an index of 1 is used for the file used -** to hold temporary tables. -** -** This instruction automatically closes all cursors and releases both -** the read and write locks on the indicated database. -*/ -case OP_Rollback: { - sqlite3RollbackAll(db); break; } @@ -3568,12 +3549,16 @@ case OP_IdxRecno: { assert( pC->deferredMoveto==0 ); assert( pC->intKey==0 ); - rc = sqlite3VdbeIdxRowid(pCrsr, &rowid); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; + if( pC->nullRow ){ + pTos->flags = MEM_Null; + }else{ + rc = sqlite3VdbeIdxRowid(pCrsr, &rowid); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } + pTos->flags = MEM_Int; + pTos->i = rowid; } - pTos->flags = MEM_Int; - pTos->i = rowid; #if 0 /* Read the final 9 bytes of the key into buf[]. If the whole key is diff --git a/src/vdbeInt.h b/src/vdbeInt.h index d41dbdd3b..a7f0dbf75 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -307,7 +307,6 @@ struct Vdbe { int rc; /* Value to return */ unsigned uniqueCnt; /* Used by OP_MakeRecord when P2!=0 */ int errorAction; /* Recovery action to do in case of an error */ - int undoTransOnError; /* If error, either ROLLBACK or COMMIT */ int inTempTrans; /* True if temp database is transactioned */ int returnStack[100]; /* Return address stack for OP_Gosub & OP_Return */ int returnDepth; /* Next unused element in returnStack[] */ diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 682d9a63e..56388fd71 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -168,6 +168,10 @@ int sqlite3_step(sqlite3_stmt *pStmt){ p->rc = SQLITE_MISUSE; return SQLITE_MISUSE; } + if( p->pc<0 ){ + db->activeVdbeCnt++; + p->pc = 0; + } if( p->explain ){ rc = sqlite3VdbeList(p); }else{ @@ -378,7 +382,7 @@ const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ */ static int vdbeUnbind(Vdbe *p, int i){ Mem *pVar; - if( p->magic!=VDBE_MAGIC_RUN || p->pc!=0 ){ + if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){ sqlite3Error(p->db, SQLITE_MISUSE, 0); return SQLITE_MISUSE; } diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 55ced4b21..60e785361 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -659,12 +659,11 @@ void sqlite3VdbeMakeReady( } #endif p->pTos = &p->aStack[-1]; - p->pc = 0; + p->pc = -1; p->rc = SQLITE_OK; p->uniqueCnt = 0; p->returnDepth = 0; p->errorAction = OE_Abort; - p->undoTransOnError = 0; p->popStack = 0; p->explain |= isExplain; p->magic = VDBE_MAGIC_RUN; @@ -896,6 +895,32 @@ int sqlite3VdbeSetColName(Vdbe *p, int idx, const char *zName, int N){ return rc; } +/* +** This routine checks that the sqlite3.activeVdbeCnt count variable +** matches the number of vdbe's in the list sqlite3.pVdbe that are +** currently active. An assertion fails if the two counts do not match. +** +** This is a no-op if NDEBUG is defined. +*/ +#ifndef NDEBUG +static void checkActiveVdbeCnt(sqlite *db){ + Vdbe *p; + int cnt = 0; + + p = db->pVdbe; + while( p ){ + if( (p->magic==VDBE_MAGIC_RUN && p->pc>=0) || p->magic==VDBE_MAGIC_HALT ){ + cnt++; + } + p = p->pNext; + } + + assert( cnt==db->activeVdbeCnt ); +} +#else +#define checkActiveVdbeCnt(x) +#endif + /* ** Clean up a VDBE after execution but do not delete the VDBE just yet. ** Write any error messages into *pzErrMsg. Return the result code. @@ -906,10 +931,13 @@ int sqlite3VdbeSetColName(Vdbe *p, int idx, const char *zName, int N){ int sqlite3VdbeReset(Vdbe *p, char **pzErrMsg){ sqlite *db = p->db; int i; + int (*xFunc)(Btree *pBt) = 0; /* Function to call on each btree backend */ + int needXcommit = 0; if( p->magic!=VDBE_MAGIC_RUN && p->magic!=VDBE_MAGIC_HALT ){ sqlite3SetString(pzErrMsg, sqlite3_error_string(SQLITE_MISUSE), (char*)0); - sqlite3Error(p->db, SQLITE_MISUSE, sqlite3_error_string(SQLITE_MISUSE),0); + sqlite3Error(p->db, SQLITE_MISUSE, 0 ,0); + db->activeVdbeCnt--; return SQLITE_MISUSE; } if( p->zErrMsg ){ @@ -922,48 +950,61 @@ int sqlite3VdbeReset(Vdbe *p, char **pzErrMsg){ p->zErrMsg = 0; }else if( p->rc ){ sqlite3SetString(pzErrMsg, sqlite3_error_string(p->rc), (char*)0); - sqlite3Error(p->db, p->rc, "%s", sqlite3_error_string(p->rc) , 0); + sqlite3Error(p->db, p->rc, 0); }else{ sqlite3Error(p->db, SQLITE_OK, 0); } Cleanup(p); - if( p->rc!=SQLITE_OK ){ - switch( p->errorAction ){ - case OE_Abort: { - if( !p->undoTransOnError ){ - for(i=0; i<db->nDb; i++){ - if( db->aDb[i].pBt ){ - sqlite3BtreeRollbackStmt(db->aDb[i].pBt); - } - } - break; - } - /* Fall through to ROLLBACK */ - } - case OE_Rollback: { - sqlite3RollbackAll(db); - db->flags &= ~SQLITE_InTrans; - db->onError = OE_Default; - break; - } - default: { - if( p->undoTransOnError ){ - sqlite3RollbackAll(db); - db->flags &= ~SQLITE_InTrans; - db->onError = OE_Default; + + /* Figure out which function to call on the btree backends that + ** have active transactions. + */ + checkActiveVdbeCnt(db); + if( db->autoCommit && db->activeVdbeCnt==1 ){ + if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ + xFunc = sqlite3BtreeCommit; + needXcommit = 1; + }else{ + xFunc = sqlite3BtreeRollback; + } + }else{ + if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ + xFunc = sqlite3BtreeCommitStmt; + }else if( p->errorAction==OE_Abort ){ + xFunc = sqlite3BtreeRollbackStmt; + }else{ + xFunc = sqlite3BtreeRollback; + db->autoCommit = 1; + } + } + + for(i=0; xFunc && i<db->nDb; i++){ + Btree *pBt = db->aDb[i].pBt; + if( sqlite3BtreeIsInTrans(pBt) ){ + int rc; + if( db->xCommitCallback && needXcommit ){ + if( db->xCommitCallback(db->pCommitArg)!=0 ){ + p->rc = SQLITE_CONSTRAINT; + sqlite3Error(db, SQLITE_CONSTRAINT, 0); + xFunc = sqlite3BtreeRollback; } - break; + needXcommit = 0; } + rc = xFunc(pBt); + if( p->rc==SQLITE_OK ) p->rc = rc; } + } + + + if( p->rc!=SQLITE_OK ){ sqlite3RollbackInternalChanges(db); } - for(i=0; i<db->nDb; i++){ - if( db->aDb[i].pBt && db->aDb[i].inTrans==2 ){ - sqlite3BtreeCommitStmt(db->aDb[i].pBt); - db->aDb[i].inTrans = 1; - } + + if( (p->magic==VDBE_MAGIC_RUN && p->pc>=0) || p->magic==VDBE_MAGIC_HALT ){ + db->activeVdbeCnt--; } - assert( p->pTos<&p->aStack[p->pc] || sqlite3_malloc_failed==1 ); + + assert( p->pTos<&p->aStack[p->pc<0?0:p->pc] || sqlite3_malloc_failed==1 ); #ifdef VDBE_PROFILE { FILE *out = fopen("vdbe_profile.out", "a"); |