From cabb0819714a045d8c4c54edc5d8da8d7bdf82a6 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 14 Sep 2002 13:47:32 +0000 Subject: [PATCH] Do not put a write lock on the main database file when writing to a temporary table. (CVS 750) FossilOrigin-Name: 3f253afe15d4f7392555f340a41d780d1248087f --- manifest | 30 ++++++++--------- manifest.uuid | 2 +- src/build.c | 33 +++++++++++-------- src/delete.c | 5 +-- src/insert.c | 5 +-- src/sqliteInt.h | 4 +-- src/tclsqlite.c | 79 ++++++++++++++++++++++++++++++++++++++++++--- src/trigger.c | 6 ++-- src/update.c | 4 +-- src/vdbe.c | 19 ++++++++--- test/lock.test | 57 +++++++++++++++++++++++++++++++- test/tclsqlite.test | 4 +-- 12 files changed, 195 insertions(+), 53 deletions(-) diff --git a/manifest b/manifest index 0a4b94c29f..4938f28118 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Rename\sall\stests\sso\sthat\sthe\sfirst\spart\sof\sthe\stest\sname\scorresponds\sto\sthe\nfile\sthat\scontains\sthat\stest.\s\sThis\smakes\sit\smuch\seasier\sto\sfind\sa\sparticular\ntest\safter\sit\sfail.\s(CVS\s749) -D 2002-09-14T12:04:56 +C Do\snot\sput\sa\swrite\slock\son\sthe\smain\sdatabase\sfile\swhen\swriting\sto\sa\stemporary\ntable.\s(CVS\s750) +D 2002-09-14T13:47:32 F Makefile.in d6c9a85c2a5e696843201d090dcf8bf2f8716f2a F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -20,14 +20,14 @@ F spec.template 238f7db425a78dc1bb7682e56e3834c7270a3f5e F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea F src/btree.c 8024b87635c2adf133f153f1bb595125ec1c7d7b F src/btree.h 0ca6c2631338df62e4f7894252d9347ae234eda9 -F src/build.c 0116afe4f67687206364c4d1e88dc07aefc661de -F src/delete.c c9f59ee217e062eb9de7b64b76b5cfff42b2f028 +F src/build.c d41b8da6b52ff84b235a785b226c37f3090ed276 +F src/delete.c aad9d4051ab46e6f6391ea5f7b8994a7c05bdd15 F src/encode.c 346b12b46148506c32038524b95c4631ab46d760 F src/expr.c e1327eb020a68ff7c49382e121ad4b71b3441b2a F src/func.c e45cd908b9b723d9b91473d09e12c23f786b3fc2 F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72 F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8 -F src/insert.c a2f5455009904476b43ec5304a181b505235f72f +F src/insert.c 764300a0bd8074a2174946c0bf8a550bd833397a F src/main.c ff7c05ef88fa1374e5540ce20173ae8e1836f8a4 F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565 F src/os.c 091a89297bf80927cde146cd1dbf89c908864f3a @@ -41,18 +41,18 @@ F src/select.c 74a025cd6887b636fc06a79ff6246c4eb6826ec4 F src/shell.c 9e9a6eb6bca07f01e6472a603f908a0127ea50ff F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in d3999a9c6374675779058d6cfe5431131618e92b -F src/sqliteInt.h 62177a08d332148b1d69cd040840aac45ad86a42 +F src/sqliteInt.h 54caf09fbb64b43a060637c46fb7464ea7b6f759 F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63 -F src/tclsqlite.c 79ceb1d0092cca22785cc00a0a596ba0aca6b363 +F src/tclsqlite.c fa646506f02509455c1e4a878d1303bd2d4c3ead F src/test1.c a46e9f61915b32787c5d5a05a4b92e4dacc437d9 F src/test2.c 5fa694d130b3309e3f9c852f0a437750fcb5a006 F src/test3.c 540fa7fc3cb3732517b779b5f90ad9cc4303d0ab F src/threadtest.c 72bce0a284647314847bbea44616ceb056bfb77f F src/tokenize.c 62c98842447effe92eba9622bb2f9a2a8a4b97ad -F src/trigger.c c90a292a4bef25e478fd5deda6d300319be6a023 -F src/update.c f07e6ed2c517c92871e54d3f5886d1cf56121b11 +F src/trigger.c 5ba917fc226b96065108da28186c2efaec53e481 +F src/update.c 881e4c8e7c786545da4fd2d95da19252b2e31137 F src/util.c c70d5da5357e01b58392faebae3c3620c1d71f14 -F src/vdbe.c 7e7392f2a92187ba1d2351fed0524c2dd607cffb +F src/vdbe.c 8e567db1f36b2c6dda4719ebe53d565c087a5702 F src/vdbe.h b7584044223104ba7896a7f87b66daebdd6022ba F src/where.c 53959c9d94adaf93b409271815e26eafa6ddd515 F test/all.test efd958d048c70a3247997c482f0b33561f7759f0 @@ -75,7 +75,7 @@ F test/intpkey.test f3620158fd7963af1306b01047277f10ae91a30b F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a F test/join.test 90a620f2a2d015e5139d5a4cde0eeb4cf62523bf F test/limit.test 9f26f874bc765df5b3f5c92d26d1b12eac6d4cf9 -F test/lock.test 5079615ba0ef0899c4cbade42ffec291620a2819 +F test/lock.test 388a3a10962d2d571c0c1821cc35bf069ee73473 F test/main.test c66b564554b770ee7fdbf6a66c0cd90329bc2c85 F test/malloc.test 7ba32a9ebd3aeed52ae4aaa6d42ca37e444536fd F test/memleak.test b4f59aa44488793b00feff2011d77d0f05b22468 @@ -100,7 +100,7 @@ F test/sort.test 876b76c5a837af5bead713146c7c65f85e84fbf5 F test/subselect.test f0fea8cf9f386d416d64d152e3c65f9116d0f50f F test/table.test 10508e5e53fb7971b9fa6acb29d85748e545745c F test/tableapi.test 3c80421a889e1d106df16e5800fa787f0d2914a6 -F test/tclsqlite.test 6f4b9760681c7dbca52a18d0ab46a1679cdc79b9 +F test/tclsqlite.test 2441ab135e5af85110326b3e3b057e7257c144e1 F test/temptable.test 03b7bdb7d6ce2c658ad20c94b037652c6cad34e0 F test/tester.tcl 6f603d90881bd835ea27c568a7fecaa57dce91cc F test/trans.test 10b53c77e2cc4ad9529c15fdcb390b8d5722ea65 @@ -149,7 +149,7 @@ F www/speed.tcl a20a792738475b68756ea7a19321600f23d1d803 F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P ef7116751ddc4e82228c115b0a332ffb47a22ae5 -R 8189584121ea08912ee4948047822dc1 +P 6cb80ae10af60863cc25c22a6442ba1d43b7409c +R 780c4a4a4908db085bbda24c5386e9ab U drh -Z 5104f4a49d2a5667b7c13a5b97f926e5 +Z 662e185948fcdefe24022119237f420c diff --git a/manifest.uuid b/manifest.uuid index ff635bc915..ef575427d7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6cb80ae10af60863cc25c22a6442ba1d43b7409c \ No newline at end of file +3f253afe15d4f7392555f340a41d780d1248087f \ No newline at end of file diff --git a/src/build.c b/src/build.c index 4c1102d8fc..39ddd8b6b2 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.111 2002/08/31 18:53:06 drh Exp $ +** $Id: build.c,v 1.112 2002/09/14 13:47:32 drh Exp $ */ #include "sqliteInt.h" #include @@ -398,7 +398,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName, int isTemp){ ** now. */ if( !pParse->initFlag && (v = sqliteGetVdbe(pParse))!=0 ){ - sqliteBeginWriteOperation(pParse, 0); + sqliteBeginWriteOperation(pParse, 0, isTemp); if( !isTemp ){ sqliteVdbeAddOp(v, OP_Integer, db->file_format, 0); sqliteVdbeAddOp(v, OP_SetCookie, 0, 1); @@ -1105,7 +1105,7 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){ }; Index *pIdx; Trigger *pTrigger; - sqliteBeginWriteOperation(pParse, 0); + sqliteBeginWriteOperation(pParse, 0, pTable->isTemp); sqliteOpenMasterTable(v, pTable->isTemp); /* Drop all triggers associated with the table being dropped */ pTrigger = pTable->pTrigger; @@ -1537,7 +1537,7 @@ void sqliteCreateIndex( v = sqliteGetVdbe(pParse); if( v==0 ) goto exit_create_index; if( pTable!=0 ){ - sqliteBeginWriteOperation(pParse, 0); + sqliteBeginWriteOperation(pParse, 0, isTemp); sqliteOpenMasterTable(v, isTemp); } sqliteVdbeAddOp(v, OP_NewRecno, 0, 0); @@ -1643,7 +1643,7 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ int base; Table *pTab = pIndex->pTable; - sqliteBeginWriteOperation(pParse, 0); + sqliteBeginWriteOperation(pParse, 0, pTab->isTemp); sqliteOpenMasterTable(v, pTab->isTemp); base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); sqliteVdbeChangeP3(v, base+1, pIndex->zName, 0); @@ -1824,7 +1824,7 @@ void sqliteCopy( v = sqliteGetVdbe(pParse); if( v ){ int openOp; - sqliteBeginWriteOperation(pParse, 1); + sqliteBeginWriteOperation(pParse, 1, pTab->isTemp); addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0); sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n); sqliteVdbeDequoteP3(v, addr); @@ -1910,7 +1910,7 @@ void sqliteBeginTransaction(Parse *pParse, int onError){ "within a transaction", 0); return; } - sqliteBeginWriteOperation(pParse, 0); + sqliteBeginWriteOperation(pParse, 0, 0); db->flags |= SQLITE_InTrans; db->onError = onError; } @@ -1969,16 +1969,23 @@ void sqliteRollbackTransaction(Parse *pParse){ ** rollback the whole transaction. For operations where all constraints ** can be checked before any changes are made to the database, it is never ** necessary to undo a write and the checkpoint should not be set. +** +** The tempOnly flag indicates that only temporary tables will be changed +** during this write operation. The primary database table is not +** write-locked. Only the temporary database file gets a write lock. +** Other processes can continue to read or write the primary database file. */ -void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint){ +void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int tempOnly){ Vdbe *v; v = sqliteGetVdbe(pParse); if( v==0 ) return; if( pParse->trigStack ) return; /* if this is in a trigger */ if( (pParse->db->flags & SQLITE_InTrans)==0 ){ - sqliteVdbeAddOp(v, OP_Transaction, 0, 0); - sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0); - pParse->schemaVerified = 1; + sqliteVdbeAddOp(v, OP_Transaction, tempOnly, 0); + if( !tempOnly ){ + sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0); + pParse->schemaVerified = 1; + } }else if( setCheckpoint ){ sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0); } @@ -2081,7 +2088,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ int addr; int size = atoi(zRight); if( size<0 ) size = -size; - sqliteBeginWriteOperation(pParse, 0); + sqliteBeginWriteOperation(pParse, 0, 0); sqliteVdbeAddOp(v, OP_Integer, size, 0); sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2); addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0); @@ -2172,7 +2179,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ int addr; int size = db->cache_size; if( size<0 ) size = -size; - sqliteBeginWriteOperation(pParse, 0); + sqliteBeginWriteOperation(pParse, 0, 0); sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2); sqliteVdbeAddOp(v, OP_Dup, 0, 0); addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0); diff --git a/src/delete.c b/src/delete.c index 4998fc55ce..55b5e60c83 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** -** $Id: delete.c,v 1.41 2002/07/19 18:52:41 drh Exp $ +** $Id: delete.c,v 1.42 2002/09/14 13:47:32 drh Exp $ */ #include "sqliteInt.h" @@ -150,7 +150,8 @@ void sqliteDeleteFrom( if( v==0 ){ goto delete_from_cleanup; } - sqliteBeginWriteOperation(pParse, row_triggers_exist); + sqliteBeginWriteOperation(pParse, row_triggers_exist, + !row_triggers_exist && pTab->isTemp); /* Initialize the counter of the number of rows deleted, if ** we are counting rows. diff --git a/src/insert.c b/src/insert.c index fc2d5cccdb..5cf44e7074 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.66 2002/08/28 03:00:58 drh Exp $ +** $Id: insert.c,v 1.67 2002/09/14 13:47:32 drh Exp $ */ #include "sqliteInt.h" @@ -160,7 +160,8 @@ void sqliteInsert( */ v = sqliteGetVdbe(pParse); if( v==0 ) goto insert_cleanup; - sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist); + sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist, + !row_triggers_exist && pTab->isTemp); /* if there are row triggers, allocate a temp table for new.* references. */ if( row_triggers_exist ){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 6b40c87684..63b39f9d43 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.145 2002/08/31 18:53:07 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.146 2002/09/14 13:47:32 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -977,7 +977,7 @@ void sqliteGenerateRowDelete(sqlite*, Vdbe*, Table*, int, int); void sqliteGenerateRowIndexDelete(sqlite*, Vdbe*, Table*, int, char*); void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int); void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int); -void sqliteBeginWriteOperation(Parse*, int); +void sqliteBeginWriteOperation(Parse*, int, int); void sqliteEndWriteOperation(Parse*); Expr *sqliteExprDup(Expr*); void sqliteTokenCopy(Token*, Token*); diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 8fe2ab908f..9037bdac66 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -11,7 +11,7 @@ ************************************************************************* ** A TCL Interface to SQLite ** -** $Id: tclsqlite.c,v 1.41 2002/09/03 19:43:24 drh Exp $ +** $Id: tclsqlite.c,v 1.42 2002/09/14 13:47:32 drh Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ @@ -31,6 +31,17 @@ # define UTF_TRANSLATION_NEEDED 1 #endif +/* +** New SQL functions can be created as TCL scripts. Each such function +** is described by an instance of the following structure. +*/ +typedef struct SqlFunc SqlFunc; +struct SqlFunc { + Tcl_Interp *interp; /* The TCL interpret to execute the function */ + char *zScript; /* The script to be run */ + SqlFunc *pNext; /* Next function on the list of them all */ +}; + /* ** There is one instance of this structure for each SQLite database ** that has been opened by the SQLite TCL interface. @@ -40,6 +51,7 @@ struct SqliteDb { sqlite *db; /* The "real" database structure */ Tcl_Interp *interp; /* The interpreter used for this database */ char *zBusy; /* The busy callback routine */ + SqlFunc *pFunc; /* List of SQL functions */ }; /* @@ -239,6 +251,11 @@ static int DbEvalCallback2( static void DbDeleteCmd(void *db){ SqliteDb *pDb = (SqliteDb*)db; sqlite_close(pDb->db); + while( pDb->pFunc ){ + SqlFunc *pFunc = pDb->pFunc; + pDb->pFunc = pFunc->pNext; + Tcl_Free((char*)pFunc); + } if( pDb->zBusy ){ Tcl_Free(pDb->zBusy); } @@ -270,6 +287,29 @@ static int DbBusyHandler(void *cd, const char *zTable, int nTries){ return 1; } +/* +** This routine is called to evaluate an SQL function implemented +** using TCL script. +*/ +static void tclSqlFunc(sqlite_func *context, int argc, const char **argv){ + SqlFunc *p = sqlite_user_data(context); + Tcl_DString cmd; + int i; + int rc; + + Tcl_DStringInit(&cmd); + Tcl_DStringAppend(&cmd, p->zScript, -1); + for(i=0; iinterp, Tcl_DStringValue(&cmd)); + if( rc ){ + sqlite_set_result_error(context, Tcl_GetStringResult(p->interp), -1); + }else{ + sqlite_set_result_string(context, Tcl_GetStringResult(p->interp), -1); + } +} + /* ** The "sqlite" command below creates a new Tcl command for each ** connection it opens to an SQLite database. This routine is invoked @@ -288,13 +328,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ int choice; static const char *DB_strs[] = { "busy", "changes", "close", - "complete", "eval", "last_insert_rowid", - "open_aux_file", "timeout", 0 + "complete", "eval", "function", + "last_insert_rowid", "open_aux_file", "timeout", + 0 }; enum DB_enum { DB_BUSY, DB_CHANGES, DB_CLOSE, - DB_COMPLETE, DB_EVAL, DB_LAST_INSERT_ROWID, - DB_OPEN_AUX_FILE, DB_TIMEOUT, + DB_COMPLETE, DB_EVAL, DB_FUNCTION, + DB_LAST_INSERT_ROWID, DB_OPEN_AUX_FILE, DB_TIMEOUT, }; if( objc<2 ){ @@ -468,6 +509,34 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ return rc; } + /* + ** $db function NAME SCRIPT + ** + ** Create a new SQL function called NAME. Whenever that function is + ** called, invoke SCRIPT to evaluate the function. + */ + case DB_FUNCTION: { + SqlFunc *pFunc; + char *zName; + char *zScript; + int nScript; + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT"); + return TCL_ERROR; + } + zName = Tcl_GetStringFromObj(objv[2], 0); + zScript = Tcl_GetStringFromObj(objv[3], &nScript); + pFunc = (SqlFunc*)Tcl_Alloc( sizeof(*pFunc) + nScript + 1 ); + if( pFunc==0 ) return TCL_ERROR; + pFunc->interp = interp; + pFunc->pNext = pDb->pFunc; + pFunc->zScript = (char*)&pFunc[1]; + strcpy(pFunc->zScript, zScript); + sqlite_create_function(pDb->db, zName, -1, tclSqlFunc, pFunc); + sqlite_function_type(pDb->db, zName, SQLITE_NUMERIC); + break; + } + /* ** $db last_insert_rowid ** diff --git a/src/trigger.c b/src/trigger.c index 8cbf1fbf65..222d09de0d 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -143,7 +143,7 @@ void sqliteCreateTrigger( /* Make an entry in the sqlite_master table */ v = sqliteGetVdbe(pParse); if( v==0 ) goto trigger_cleanup; - sqliteBeginWriteOperation(pParse, 0); + sqliteBeginWriteOperation(pParse, 0, 0); sqliteOpenMasterTable(v, tab->isTemp); addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig); sqliteVdbeChangeP3(v, addr, tab->isTemp ? TEMP_MASTER_NAME : MASTER_NAME, @@ -386,7 +386,7 @@ void sqliteDropTrigger(Parse *pParse, Token *pName, int nested){ { OP_Next, 0, ADDR(3), 0}, /* 7 */ }; - sqliteBeginWriteOperation(pParse, 0); + sqliteBeginWriteOperation(pParse, 0, 0); sqliteOpenMasterTable(v, pTable->isTemp); base = sqliteVdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); sqliteVdbeChangeP3(v, base+1, zName, 0); @@ -674,7 +674,7 @@ void sqliteViewTriggers( v = sqliteGetVdbe(pParse); assert(v); - sqliteBeginWriteOperation(pParse, 1); + sqliteBeginWriteOperation(pParse, 1, 0); /* Allocate temp tables */ oldIdx = pParse->nTab++; diff --git a/src/update.c b/src/update.c index 5407544c36..ce26346543 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.49 2002/07/21 23:09:55 danielk1977 Exp $ +** $Id: update.c,v 1.50 2002/09/14 13:47:32 drh Exp $ */ #include "sqliteInt.h" @@ -186,7 +186,7 @@ void sqliteUpdate( */ v = sqliteGetVdbe(pParse); if( v==0 ) goto update_cleanup; - sqliteBeginWriteOperation(pParse, 1); + sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->isTemp); /* Begin the database scan */ diff --git a/src/vdbe.c b/src/vdbe.c index 6f5d3c062d..a76bfb4422 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -36,7 +36,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.177 2002/09/08 17:23:43 drh Exp $ +** $Id: vdbe.c,v 1.178 2002/09/14 13:47:32 drh Exp $ */ #include "sqliteInt.h" #include @@ -1336,6 +1336,7 @@ int sqliteVdbeExec( unsigned uniqueCnt = 0; /* Used by OP_MakeRecord when P2!=0 */ int errorAction = OE_Abort; /* Recovery action to do in case of an error */ int undoTransOnError = 0; /* If error, either ROLLBACK or COMMIT */ + int inTempTrans = 0; /* True if temp database is transactioned */ char zBuf[100]; /* Space to sprintf() an integer */ int returnStack[100]; /* Return address stack for OP_Gosub & OP_Return */ int returnDepth = 0; /* Next unused element in returnStack[] */ @@ -2919,12 +2920,17 @@ case OP_Checkpoint: { break; } -/* Opcode: Transaction * * * +/* Opcode: Transaction P1 * * ** ** Begin a transaction. The transaction ends when a Commit or Rollback ** opcode is encountered. Depending on the ON CONFLICT setting, the ** transaction might also be rolled back if an error is encountered. ** +** If P1 is true, then the transaction is started on the temporary +** tables of the database only. The main database file is not write +** locked and other processes can continue to read the main database +** file. +** ** A write lock is obtained on the database file when a transaction is ** started. No other process can read or write the file while the ** transaction is underway. Starting a transaction also creates a @@ -2933,13 +2939,14 @@ case OP_Checkpoint: { */ case OP_Transaction: { int busy = 0; - if( db->pBeTemp ){ + if( db->pBeTemp && !inTempTrans ){ rc = sqliteBtreeBeginTrans(db->pBeTemp); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } + inTempTrans = 1; } - do{ + if( pOp->p1==0 ) do{ rc = sqliteBtreeBeginTrans(pBt); switch( rc ){ case SQLITE_BUSY: { @@ -2954,6 +2961,7 @@ case OP_Transaction: { /* Fall thru into the next case */ } case SQLITE_OK: { + inTempTrans = 0; busy = 0; break; } @@ -2976,7 +2984,7 @@ case OP_Transaction: { */ case OP_Commit: { if( db->pBeTemp==0 || (rc = sqliteBtreeCommit(db->pBeTemp))==SQLITE_OK ){ - rc = sqliteBtreeCommit(pBt); + rc = inTempTrans ? SQLITE_OK : sqliteBtreeCommit(pBt); } if( rc==SQLITE_OK ){ sqliteCommitInternalChanges(db); @@ -2985,6 +2993,7 @@ case OP_Commit: { sqliteBtreeRollback(pBt); sqliteRollbackInternalChanges(db); } + inTempTrans = 0; break; } diff --git a/test/lock.test b/test/lock.test index f51c9fbdc5..ca715fffff 100644 --- a/test/lock.test +++ b/test/lock.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is database locks. # -# $Id: lock.test,v 1.17 2002/08/29 23:59:50 drh Exp $ +# $Id: lock.test,v 1.18 2002/09/14 13:47:33 drh Exp $ set testdir [file dirname $argv0] @@ -270,7 +270,62 @@ do_test lock-4.3 { set rc [catch {db2 eval {SELECT * FROM t1}} msg] lappend rc $msg $::callback_value } {1 {database is locked} {1 2 3 4 5}} +execsql {ROLLBACK} +# When one thread is writing, other threads cannot read. Except if the +# writing thread is writing to its temporary tables, the other threads +# can still read. +# +proc tx_exec {sql} { + db2 eval $sql +} +do_test lock-5.1 { + execsql { + SELECT * FROM t1 + } +} {2 1} +do_test lock-5.2 { + db function tx_exec tx_exec + catchsql { + INSERT INTO t1(a,b) SELECT 3, tx_exec('SELECT y FROM t2 LIMIT 1'); + } +} {1 {database is locked}} +do_test lock-5.3 { + execsql { + CREATE TEMP TABLE t3(x); + SELECT * FROM t3; + } +} {} +do_test lock-5.4 { + catchsql { + INSERT INTO t3 SELECT tx_exec('SELECT y FROM t2 LIMIT 1'); + } +} {0 {}} +do_test lock-5.5 { + execsql { + SELECT * FROM t3; + } +} {8} +do_test lock-5.6 { + catchsql { + UPDATE t1 SET a=tx_exec('SELECT x FROM t2'); + } +} {1 {database is locked}} +do_test lock-5.7 { + execsql { + SELECT * FROM t1; + } +} {2 1} +do_test lock-5.8 { + catchsql { + UPDATE t3 SET x=tx_exec('SELECT x FROM t2'); + } +} {0 {}} +do_test lock-5.9 { + execsql { + SELECT * FROM t3; + } +} {9} do_test lock-999.1 { rename db2 {} diff --git a/test/tclsqlite.test b/test/tclsqlite.test index e4844ae8f7..88041859f4 100644 --- a/test/tclsqlite.test +++ b/test/tclsqlite.test @@ -15,7 +15,7 @@ # interface is pretty well tested. This file contains some addition # tests for fringe issues that the main test suite does not cover. # -# $Id: tclsqlite.test,v 1.7 2002/06/25 19:31:18 drh Exp $ +# $Id: tclsqlite.test,v 1.8 2002/09/14 13:47:33 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -29,7 +29,7 @@ do_test tcl-1.1 { do_test tcl-1.2 { set v [catch {db bogus} msg] lappend v $msg -} {1 {bad option "bogus": must be busy, changes, close, complete, eval, last_insert_rowid, open_aux_file, or timeout}} +} {1 {bad option "bogus": must be busy, changes, close, complete, eval, function, last_insert_rowid, open_aux_file, or timeout}} do_test tcl-1.3 { execsql {CREATE TABLE t1(a int, b int)} execsql {INSERT INTO t1 VALUES(10,20)}