diff --git a/manifest b/manifest
index e164f8a878..dafdca0178 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C More\schanges\sto\stake\sadvantage\sof\sthe\sTK_\sand\sOP_\salignments\sto\savoid\nunnecessary\stranslations.\s(CVS\s1999)
-D 2004-10-04T13:38:09
+C Add\ssupport\sfor\sDEFERRED,\sIMMEDIATE,\sand\sEXCLUSIVE\stransactions.\s(CVS\s2000)
+D 2004-10-05T02:41:42
F Makefile.in 78ddc9fca09ab6e3b75a79ecf8d490e34cd0519c
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -29,16 +29,16 @@ F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863
F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689
F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea
-F src/btree.c 014d3c9d31136050f3b3294c0c5dc0c7615557bf
+F src/btree.c bb3f8cadf65cc0752d07e219733a496c1aebe020
F src/btree.h 94dfec0a1722d33359b23e7e310f2b64ffedf029
-F src/build.c 2ed6d9c26ad736142012bc99898869db17337121
+F src/build.c 73bd4219c1cb6fb09a05b7d4a6ed3ae1d0ecb8db
F src/date.c 93927e2d1ffbd833fc220644896cfdc8f8d4af34
-F src/delete.c d862b383a9abc0b79f4588783c2619fe52d74ea7
+F src/delete.c 7a9543ed784bd51ded17c805ff6a4fe864c1676c
F src/expr.c 2f492bf532d700bd2c38e16caa49048535e8ed27
F src/func.c 1fbc5256639586573fd0e70814d6dcd8bc10afc1
F src/hash.c a97721a55440b7bea31ffe471bb2f6b4123cddd5
F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
-F src/insert.c 2c10c001f62cde92e9517ec7516b6584519754d1
+F src/insert.c 7e8ce8834c3716a313694e5340dbf28ff828677a
F src/legacy.c d58ea507bce885298a2c8c3cbb0f4bff5d47830b
F src/main.c 2a1b9623fde3fe5e22fe726cdae4e10f33671caa
F src/md5.c 7ae1c39044b95de2f62e066f47bb1deb880a1070
@@ -52,16 +52,16 @@ F src/os_unix.c 5c1f362846afec4232c2291b9f1564f0487e91f4
F src/os_unix.h f3097815e041e82e24d92505e1ff61ba24172d13
F src/os_win.c 9482dfc92f289b68205bb2c9315757c7e3946bfb
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
-F src/pager.c d3688828d314135ef0cd87b4d892805136c4168f
-F src/pager.h 67739fe649f33be55dba522ca8a9cc4e42d14f71
-F src/parse.y 3616bdde17fd9be9c235e40cd732a831a3e8fa61
-F src/pragma.c 45978cc82fdf91f00d024a8e875c2b679fbce488
+F src/pager.c dc0ffab9941393b072e0b1f1f3de54830727cec9
+F src/pager.h 774d1973acbda341827d21b0da0150575d69f7d9
+F src/parse.y e03d7d7f712ad2b5be61e9a024820c0a8ffdf36d
+F src/pragma.c ba7fdd19c1680bb9bfc8bc5230278f6ae6b1c8ff
F src/printf.c 40770e1f553612d13dfc86d236086e69baa62fe1
F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
F src/select.c 96b1489111abe9b584be2f2cce26ad6f2d425b4e
F src/shell.c 0856973c2b94e22664bc43f398ba3fc22d5e080c
F src/sqlite.h.in 9bb76ff9e79ee72e6d529ff6ab1c252d513c3864
-F src/sqliteInt.h bfb12f1da75b2a8f5c88c9c3050343571def65b9
+F src/sqliteInt.h 610f25a92c0ce5edf40d12087c643c310e1d7d05
F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
F src/tclsqlite.c 0302e3f42f015d132d1291f3388c06e86c24a008
F src/test1.c 3d78e5d827bf5d037f697c233c5934d45af46cb5
@@ -69,13 +69,13 @@ F src/test2.c b11fa244fff02190707dd0879987c37c75e61fc8
F src/test3.c 5b5b0f3d11b097399c1054fff73d8f3711092301
F src/test4.c 7c6b9fc33dd1f3f93c7f1ee6e5e6d016afa6c1df
F src/test5.c b001fa7f1b9e2dc5c2331de62fc641b5ab2bd7a1
-F src/tokenize.c d4619367d9ba17c6bd4e018fb7e91015ae8562aa
-F src/trigger.c d1f770ee37a80391dd6d0948ee821b0272f99ae7
-F src/update.c 7157084216c4b02a23cdb23eb6d246aa9034fa4d
+F src/tokenize.c 449843c85c8118b9718f3935f4b5d18586c8cee2
+F src/trigger.c b983f8d763d2c253535947e543c3386dc1117ca1
+F src/update.c 174c3b593b8f4928e510a51ec309e8ce69d2371c
F src/utf.c f4f83acd73389090e32d6589d307fc55d794c7ed
F src/util.c f4ab796b9def353feed2191d7ce8e39a0f5059cd
F src/vacuum.c 257de36230cb988842f66eb08dc6c0250b8e05f3
-F src/vdbe.c 49d4bda66884b1017e11962c7fb7eaf15b436be8
+F src/vdbe.c f7ebf1a20a5fdca27e80cb96e06e988da2426379
F src/vdbe.h 067ca8d6750ba4f69a50284765e5883dee860181
F src/vdbeInt.h 6017100adff362b8dfa37a69e3f1431f084bfa5b
F src/vdbeapi.c 81ab9e84c55f5762f552904e6e5d309269b02017
@@ -137,6 +137,7 @@ F test/laststmtchanges.test 417aa27eb2b5cdfafb46e390e2c9ddd0a20eba43
F test/limit.test f7c06fccd76755e8d083b61c06bc31cf461b9c35
F test/lock.test 7cb9395919a0986ee4dd08bd49d34df93c8fc4fe
F test/lock2.test 2213590d442147d09fd2334c905a755586c1c398
+F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9
F test/main.test 1430a4b5bd3a6d5e0294966b742d80a551f87211
F test/malloc.test 769b240d89a7ef3320d88919fdb6765f9395a51f
F test/memdb.test b8a13fa79f006bd087bbcf135ce8eb62056a6027
@@ -150,7 +151,7 @@ F test/misuse.test fcd9e7cec6ecccc34822584aec6b4e31f13629e1
F test/notnull.test 7a08117a71e74b0321aaa937dbeb41a09d6eb1d0
F test/null.test 642428b6a5408cc5b954b49e1b6e5025e4458b2b
F test/pager.test 394455707a079804e8a4e431d12edce831a065f0
-F test/pager2.test 2b505eca6bf214dfff412cf615678a835c83ca74
+F test/pager2.test c7e731ac56a2984a605b032ffd19b9deee820377
F test/pager3.test 16f546293bb751b8151dc17df613fca938bbec8b
F test/pagesize.test f8b46ec46b9fe9f708a8d757dda232588dfb7217
F test/pragma.test 66a66b7f3b273b93325c9a5794acb418f52fdcbf
@@ -234,7 +235,7 @@ F www/faq.tcl 8cf9f59fd93868c9954223a99db244c9975fa43b
F www/fileformat.tcl f71a06a0d533c7df408539c64113b4adeaf29764
F www/formatchng.tcl d1dfecedfb25e122ab513a1e0948b15cb4f0be46
F www/index.tcl 3bf50fdac2f5df49cf4f6f76a7f312b5fd4725b8
-F www/lang.tcl 604683def6e987db1703faf580d9b2150905fda1
+F www/lang.tcl dde78c1415ee2d19fc30360808ca6f0e2ea71c30
F www/lockingv3.tcl afcd22f0f063989cff2f4d57bbc38d719b4c6e75
F www/mingw.tcl d96b451568c5d28545fefe0c80bee3431c73f69c
F www/nulls.tcl ede975a29def48838c606d4a0c0185d44f90a789
@@ -249,7 +250,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25
F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4
-P 4c817e3f293a9c1365e632f7dc13ae440263332a
-R baa88e3ccdeca2bd21538a311420eedc
+P e8e972ba65fc36171f6b685e8a8f67f93452e031
+R d2fc50e21e7b6df513c30d8cad94f529
U drh
-Z f63cffc592cc58b47f0dd722884db290
+Z d7255f319d142e01096c8a1787a44a2e
diff --git a/manifest.uuid b/manifest.uuid
index d6cfef77c7..9ed2e6008a 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-e8e972ba65fc36171f6b685e8a8f67f93452e031
\ No newline at end of file
+81ff8107ad63113782cf5a9ba7a512496114ba08
\ No newline at end of file
diff --git a/src/btree.c b/src/btree.c
index beb30a920b..a5bd886139 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.191 2004/09/27 13:19:52 drh Exp $
+** $Id: btree.c,v 1.192 2004/10/05 02:41:42 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -1289,8 +1289,12 @@ static int newDatabase(Btree *pBt){
/*
** Attempt to start a new transaction. A write-transaction
-** is started if the second argument is true, otherwise a read-
-** transaction.
+** is started if the second argument is nonzero, otherwise a read-
+** transaction. If the second argument is 2 or more and exclusive
+** transaction is started, meaning that no other process is allowed
+** to access the database. A preexisting transaction may not be
+** upgrade to exclusive by calling this routine a second time - the
+** exclusivity flag only works for a new transaction.
**
** A write-transaction must be started before attempting any
** changes to the database. None of the following routines
@@ -1329,7 +1333,7 @@ int sqlite3BtreeBeginTrans(Btree *pBt, int wrflag){
}
if( rc==SQLITE_OK && wrflag ){
- rc = sqlite3pager_begin(pBt->pPage1->aData);
+ rc = sqlite3pager_begin(pBt->pPage1->aData, wrflag>1);
if( rc==SQLITE_OK ){
rc = newDatabase(pBt);
}
diff --git a/src/build.c b/src/build.c
index f4deb51829..2cf64573cd 100644
--- a/src/build.c
+++ b/src/build.c
@@ -23,7 +23,7 @@
** ROLLBACK
** PRAGMA
**
-** $Id: build.c,v 1.255 2004/09/30 14:22:47 drh Exp $
+** $Id: build.c,v 1.256 2004/10/05 02:41:42 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -1376,8 +1376,6 @@ void sqlite3EndTable(Parse *pParse, Token *pEnd, Select *pSelect){
sqlite3VdbeAddOp(v, OP_Close, 0, 0);
sqlite3VdbeOp3(v, OP_ParseSchema, p->iDb, 0,
sqlite3MPrintf("tbl_name='%q'",p->zName), P3_DYNAMIC);
-
- sqlite3EndWriteOperation(pParse);
}
/* Add the table to the in-memory representation of the database.
@@ -1663,7 +1661,6 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView){
}
}
sqlite3VdbeOp3(v, OP_DropTable, pTab->iDb, 0, pTab->zName, 0);
- sqlite3EndWriteOperation(pParse);
}
sqliteViewResetAll(db, iDb);
@@ -2131,7 +2128,6 @@ void sqlite3CreateIndex(
sqlite3VdbeAddOp(v, OP_Close, 1, 0);
sqlite3ChangeCookie(db, v, iDb);
sqlite3VdbeAddOp(v, OP_Close, 0, 0);
- sqlite3EndWriteOperation(pParse);
sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0,
sqlite3MPrintf("name='%q'", pIndex->zName), P3_DYNAMIC);
}
@@ -2232,7 +2228,6 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName){
sqlite3VdbeAddOp(v, OP_Close, 0, 0);
sqlite3VdbeAddOp(v, OP_Destroy, pIndex->tnum, pIndex->iDb);
sqlite3VdbeOp3(v, OP_DropIndex, pIndex->iDb, 0, pIndex->zName, 0);
- sqlite3EndWriteOperation(pParse);
}
exit_drop_index:
@@ -2398,9 +2393,10 @@ void sqlite3SrcListDelete(SrcList *pList){
/*
** Begin a transaction
*/
-void sqlite3BeginTransaction(Parse *pParse){
+void sqlite3BeginTransaction(Parse *pParse, int type){
sqlite3 *db;
Vdbe *v;
+ int i;
if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
if( pParse->nErr || sqlite3_malloc_failed ) return;
@@ -2408,6 +2404,11 @@ void sqlite3BeginTransaction(Parse *pParse){
v = sqlite3GetVdbe(pParse);
if( !v ) return;
+ if( type!=TK_DEFERRED ){
+ for(i=0; inDb; i++){
+ sqlite3VdbeAddOp(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
+ }
+ }
sqlite3VdbeAddOp(v, OP_AutoCommit, 0, 0);
}
@@ -2551,21 +2552,6 @@ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
}
}
-/*
-** Generate code that concludes an operation that may have changed
-** the database. If a statement transaction was started, then emit
-** an OP_Commit that will cause the changes to be committed to disk.
-**
-** Note that checkpoints are automatically committed at the end of
-** a statement. Note also that there can be multiple calls to
-** sqlite3BeginWriteOperation() but there should only be a single
-** call to sqlite3EndWriteOperation() at the conclusion of the statement.
-*/
-void sqlite3EndWriteOperation(Parse *pParse){
- /* Delete me! */
- return;
-}
-
/*
** Return the transient sqlite3_value object used for encoding conversions
** during SQL compilation.
diff --git a/src/delete.c b/src/delete.c
index fefa454313..2b0457f0bc 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.81 2004/09/19 02:15:25 drh Exp $
+** $Id: delete.c,v 1.82 2004/10/05 02:41:42 drh Exp $
*/
#include "sqliteInt.h"
@@ -307,7 +307,6 @@ void sqlite3DeleteFrom(
sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
}
}
- sqlite3EndWriteOperation(pParse);
/*
** Return the number of rows that were deleted.
diff --git a/src/insert.c b/src/insert.c
index 06f7d5a4b5..114942e8c4 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.118 2004/09/19 02:15:26 drh Exp $
+** $Id: insert.c,v 1.119 2004/10/05 02:41:42 drh Exp $
*/
#include "sqliteInt.h"
@@ -616,8 +616,6 @@ void sqlite3Insert(
}
}
- sqlite3EndWriteOperation(pParse);
-
/*
** Return the number of rows inserted.
*/
diff --git a/src/pager.c b/src/pager.c
index 8b20c16c52..cf0da5200d 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.166 2004/10/02 20:38:28 drh Exp $
+** @(#) $Id: pager.c,v 1.167 2004/10/05 02:41:43 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -88,11 +88,27 @@
** or sqlite_pager_commit(), the state goes back to PAGER_SHARED.
*/
#define PAGER_UNLOCK 0
-#define PAGER_SHARED 1
-#define PAGER_RESERVED 2
-#define PAGER_EXCLUSIVE 3
-#define PAGER_SYNCED 4
+#define PAGER_SHARED 1 /* same as SHARED_LOCK */
+#define PAGER_RESERVED 2 /* same as RESERVED_LOCK */
+#define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */
+#define PAGER_SYNCED 5
+/*
+** If the SQLITE_BUSY_RESERVED_LOCK macro is set to true at compile-time,
+** then failed attempts to get a reserved lock will invoke the busy callback.
+** This is off by default. To see why, consider the following scenario:
+**
+** Suppose thread A already has a shared lock and wants a reserved lock.
+** Thread B already has a reserved lock and wants an exclusive lock. If
+** both threads are using their busy callbacks, it might be a long time
+** be for one of the threads give up and allows the other to proceed.
+** But if the thread trying to get the reserved lock gives up quickly
+** (if it never invokes its busy callback) then the contention will be
+** resolved quickly.
+*/
+#ifndef SQLITE_BUSY_RESERVED_LOCK
+# define SQLITE_BUSY_RESERVED_LOCK 0
+#endif
/*
** Each in-memory image of a page begins with the following header.
@@ -1907,6 +1923,37 @@ static int syncJournal(Pager *pPager){
return rc;
}
+/*
+** Try to obtain a lock on a file. Invoke the busy callback if the lock
+** is currently not available. Repeate until the busy callback returns
+** false or until the lock succeeds.
+**
+** Return SQLITE_OK on success and an error code if we cannot obtain
+** the lock.
+*/
+static int pager_wait_on_lock(Pager *pPager, int locktype){
+ int rc;
+ assert( PAGER_SHARED==SHARED_LOCK );
+ assert( PAGER_RESERVED==RESERVED_LOCK );
+ assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK );
+ if( pPager->state>=locktype ){
+ rc = SQLITE_OK;
+ }else{
+ int busy = 1;
+ do {
+ rc = sqlite3OsLock(&pPager->fd, locktype);
+ }while( rc==SQLITE_BUSY &&
+ pPager->pBusyHandler &&
+ pPager->pBusyHandler->xFunc &&
+ pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, busy++)
+ );
+ if( rc==SQLITE_OK ){
+ pPager->state = locktype;
+ }
+ }
+ return rc;
+}
+
/*
** Given a list of pages (connected by the PgHdr.pDirty pointer) write
** every one of those pages out to the database file and mark them all
@@ -1915,7 +1962,6 @@ static int syncJournal(Pager *pPager){
static int pager_write_pagelist(PgHdr *pList){
Pager *pPager;
int rc;
- int busy = 1;
if( pList==0 ) return SQLITE_OK;
pPager = pList->pPager;
@@ -1936,17 +1982,10 @@ static int pager_write_pagelist(PgHdr *pList){
** EXCLUSIVE, it means the database file has been changed and any rollback
** will require a journal playback.
*/
- do {
- rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK);
- }while( rc==SQLITE_BUSY &&
- pPager->pBusyHandler &&
- pPager->pBusyHandler->xFunc &&
- pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, busy++)
- );
+ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
if( rc!=SQLITE_OK ){
return rc;
}
- pPager->state = PAGER_EXCLUSIVE;
while( pList ){
assert( pList->dirty );
@@ -2019,18 +2058,10 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
** on the database file.
*/
if( pPager->nRef==0 && !pPager->memDb ){
- int busy = 1;
- do {
- rc = sqlite3OsLock(&pPager->fd, SHARED_LOCK);
- }while( rc==SQLITE_BUSY &&
- pPager->pBusyHandler &&
- pPager->pBusyHandler->xFunc &&
- pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, busy++)
- );
+ rc = pager_wait_on_lock(pPager, SHARED_LOCK);
if( rc!=SQLITE_OK ){
return rc;
}
- pPager->state = PAGER_SHARED;
/* If a journal file exists, and there is no RESERVED lock on the
** database file, then it either needs to be played back or deleted.
@@ -2408,8 +2439,12 @@ failed_to_open_journal:
** actual need to write to the journal.
**
** If the database is already reserved for writing, this routine is a no-op.
+**
+** If exFlag is true, go ahead and get an EXCLUSIVE lock on the file
+** immediately instead of waiting until we try to flush the cache. The
+** exFlag is ignored if a transaction is already active.
*/
-int sqlite3pager_begin(void *pData){
+int sqlite3pager_begin(void *pData, int exFlag){
PgHdr *pPg = DATA_TO_PGHDR(pData);
Pager *pPager = pPg->pPager;
int rc = SQLITE_OK;
@@ -2421,29 +2456,20 @@ int sqlite3pager_begin(void *pData){
pPager->state = PAGER_EXCLUSIVE;
pPager->origDbSize = pPager->dbSize;
}else{
-#ifdef SQLITE_BUSY_RESERVED_LOCK
- int busy = 1;
- do {
+ if( SQLITE_BUSY_RESERVED_LOCK || exFlag ){
+ rc = pager_wait_on_lock(pPager, RESERVED_LOCK);
+ }else{
rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK);
- }while( rc==SQLITE_BUSY &&
- pPager->pBusyHandler &&
- pPager->pBusyHandler->xFunc &&
- pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, busy++)
- );
-#else
- rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK);
-#endif
+ }
+ if( rc==SQLITE_OK ){
+ pPager->state = PAGER_RESERVED;
+ if( exFlag ){
+ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ }
+ }
if( rc!=SQLITE_OK ){
- /* We do not call the busy handler when we fail to get a reserved lock.
- ** The only reason we might fail is because another process is holding
- ** the reserved lock. But the other process will not be able to
- ** release its reserved lock until this process releases its shared
- ** lock. So we might as well fail in this process, let it release
- ** its shared lock so that the other process can commit.
- */
return rc;
}
- pPager->state = PAGER_RESERVED;
pPager->dirtyCache = 0;
TRACE2("TRANSACTION %d\n", pPager->fd.h);
if( pPager->useJournal && !pPager->tempFile ){
@@ -2504,7 +2530,7 @@ int sqlite3pager_write(void *pData){
** create it if it does not.
*/
assert( pPager->state!=PAGER_UNLOCK );
- rc = sqlite3pager_begin(pData);
+ rc = sqlite3pager_begin(pData, 0);
if( rc!=SQLITE_OK ){
return rc;
}
diff --git a/src/pager.h b/src/pager.h
index cb4a772c52..ff6dd87338 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -13,7 +13,7 @@
** subsystem. The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
-** @(#) $Id: pager.h,v 1.37 2004/07/22 01:19:35 drh Exp $
+** @(#) $Id: pager.h,v 1.38 2004/10/05 02:41:43 drh Exp $
*/
/*
@@ -74,7 +74,7 @@ int sqlite3pager_iswriteable(void*);
int sqlite3pager_overwrite(Pager *pPager, Pgno pgno, void*);
int sqlite3pager_pagecount(Pager*);
int sqlite3pager_truncate(Pager*,Pgno);
-int sqlite3pager_begin(void*);
+int sqlite3pager_begin(void*, int exFlag);
int sqlite3pager_commit(Pager*);
int sqlite3pager_sync(Pager*,const char *zMaster);
int sqlite3pager_rollback(Pager*);
diff --git a/src/parse.y b/src/parse.y
index c8a95f5523..7d56fed100 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -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.141 2004/10/04 13:38:09 drh Exp $
+** @(#) $Id: parse.y,v 1.142 2004/10/05 02:41:43 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
@@ -81,10 +81,15 @@ explain ::= . { sqlite3BeginParse(pParse, 0); }
///////////////////// Begin and end transactions. ////////////////////////////
//
-cmd ::= BEGIN trans_opt. {sqlite3BeginTransaction(pParse);}
+cmd ::= BEGIN transtype(Y) trans_opt. {sqlite3BeginTransaction(pParse, Y);}
trans_opt ::= .
trans_opt ::= TRANSACTION.
trans_opt ::= TRANSACTION nm.
+%type transtype {int}
+transtype(A) ::= . {A = TK_DEFERRED;}
+transtype(A) ::= DEFERRED(X). {A = @X;}
+transtype(A) ::= IMMEDIATE(X). {A = @X;}
+transtype(A) ::= EXCLUSIVE(X). {A = @X;}
cmd ::= COMMIT trans_opt. {sqlite3CommitTransaction(pParse);}
cmd ::= END trans_opt. {sqlite3CommitTransaction(pParse);}
cmd ::= ROLLBACK trans_opt. {sqlite3RollbackTransaction(pParse);}
@@ -127,7 +132,7 @@ id(A) ::= ID(X). {A = X;}
//
%fallback ID
ABORT AFTER ASC ATTACH BEFORE BEGIN CASCADE CONFLICT
- DATABASE DEFERRED DESC DETACH EACH END EXPLAIN FAIL FOR
+ DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR
GLOB IGNORE IMMEDIATE INITIALLY INSTEAD LIKE MATCH KEY
OF OFFSET PRAGMA RAISE REPLACE RESTRICT ROW STATEMENT
TEMP TRIGGER VACUUM VIEW.
diff --git a/src/pragma.c b/src/pragma.c
index cf0f92d3f1..1373501a77 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.67 2004/09/25 14:39:19 drh Exp $
+** $Id: pragma.c,v 1.68 2004/10/05 02:41:43 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -261,7 +261,6 @@ void sqlite3Pragma(
sqlite3VdbeAddOp(v, OP_Ge, 0, addr+3);
sqlite3VdbeAddOp(v, OP_Negative, 0, 0);
sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 2);
- sqlite3EndWriteOperation(pParse);
pDb->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->cache_size);
}
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 7b4c4fc0e7..9b712ba444 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.326 2004/10/04 13:19:24 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.327 2004/10/05 02:41:43 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
@@ -1310,7 +1310,7 @@ Vdbe *sqlite3GetVdbe(Parse*);
void sqlite3Randomness(int, void*);
void sqlite3RollbackAll(sqlite3*);
void sqlite3CodeVerifySchema(Parse*, int);
-void sqlite3BeginTransaction(Parse*);
+void sqlite3BeginTransaction(Parse*, int);
void sqlite3CommitTransaction(Parse*);
void sqlite3RollbackTransaction(Parse*);
int sqlite3ExprIsConstant(Expr*);
@@ -1323,7 +1323,6 @@ void sqlite3GenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
void sqlite3CompleteInsertion(Parse*, Table*, int, char*, int, int, int);
void sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
void sqlite3BeginWriteOperation(Parse*, int, int);
-void sqlite3EndWriteOperation(Parse*);
Expr *sqlite3ExprDup(Expr*);
void sqlite3TokenCopy(Token*, Token*);
ExprList *sqlite3ExprListDup(ExprList*);
diff --git a/src/tokenize.c b/src/tokenize.c
index 5424eb0476..6b3a5f713f 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -15,7 +15,7 @@
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
**
-** $Id: tokenize.c,v 1.89 2004/09/25 15:25:26 drh Exp $
+** $Id: tokenize.c,v 1.90 2004/10/05 02:41:43 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -71,6 +71,7 @@ static Keyword aKeywordTable[] = {
{ "EACH", TK_EACH, },
{ "ELSE", TK_ELSE, },
{ "EXCEPT", TK_EXCEPT, },
+ { "EXCLUSIVE", TK_EXCLUSIVE, },
{ "EXPLAIN", TK_EXPLAIN, },
{ "FAIL", TK_FAIL, },
{ "FOR", TK_FOR, },
diff --git a/src/trigger.c b/src/trigger.c
index 50600b0afa..97c8305906 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -241,7 +241,6 @@ void sqlite3FinishTrigger(
sqlite3VdbeAddOp(v, OP_Close, 0, 0);
sqlite3VdbeOp3(v, OP_ParseSchema, nt->iDb, 0,
sqlite3MPrintf("type='trigger' AND name='%q'", nt->name), P3_DYNAMIC);
- sqlite3EndWriteOperation(pParse);
}
if( db->init.busy ){
diff --git a/src/update.c b/src/update.c
index 5655bbe6e8..ca15000786 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.89 2004/09/19 02:15:26 drh Exp $
+** $Id: update.c,v 1.90 2004/10/05 02:41:43 drh Exp $
*/
#include "sqliteInt.h"
@@ -430,8 +430,6 @@ void sqlite3Update(
sqlite3VdbeAddOp(v, OP_Close, oldIdx, 0);
}
- sqlite3EndWriteOperation(pParse);
-
/*
** Return the number of rows that were changed.
*/
diff --git a/src/vdbe.c b/src/vdbe.c
index 327460ca25..7c5a6dea4f 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.416 2004/10/04 13:19:24 drh Exp $
+** $Id: vdbe.c,v 1.417 2004/10/05 02:41:43 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -2157,7 +2157,8 @@ case OP_AutoCommit: {
** 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.
+** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained
+** on the file.
**
** If P2 is zero, then a read-lock is obtained on the database file.
*/
diff --git a/test/lock3.test b/test/lock3.test
new file mode 100644
index 0000000000..1835c66f79
--- /dev/null
+++ b/test/lock3.test
@@ -0,0 +1,78 @@
+# 2001 September 15
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is database locks and the operation of the
+# DEFERRED, IMMEDIATE, and EXCLUSIVE keywords as modifiers to the
+# BEGIN command.
+#
+# $Id: lock3.test,v 1.1 2004/10/05 02:41:43 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Establish two connections to the same database. Put some
+# sample data into the database.
+#
+do_test lock3-1.1 {
+ sqlite3 db2 test.db
+ execsql {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ }
+ execsql {
+ SELECT * FROM t1
+ } db2
+} 1
+
+# Get a deferred lock on the database using one connection. The
+# other connection should still be able to write.
+#
+do_test lock3-2.1 {
+ execsql {BEGIN DEFERRED TRANSACTION}
+ execsql {INSERT INTO t1 VALUES(2)} db2
+ execsql {END TRANSACTION}
+ execsql {SELECT * FROM t1}
+} {1 2}
+
+# Get an immediate lock on the database using one connection. The
+# other connection should be able to read the database but not write
+# it.
+#
+do_test lock3-3.1 {
+ execsql {BEGIN IMMEDIATE TRANSACTION}
+ catchsql {SELECT * FROM t1} db2
+} {0 {1 2}}
+do_test lock3-3.2 {
+ catchsql {INSERT INTO t1 VALUES(3)} db2
+} {1 {database is locked}}
+do_test lock3-3.3 {
+ execsql {END TRANSACTION}
+} {}
+
+
+# Get an exclusive lock on the database using one connection. The
+# other connection should be unable to read or write the database.
+#
+do_test lock3-4.1 {
+ execsql {BEGIN EXCLUSIVE TRANSACTION}
+ catchsql {SELECT * FROM t1} db2
+} {1 {database is locked}}
+do_test lock3-4.2 {
+ catchsql {INSERT INTO t1 VALUES(3)} db2
+} {1 {database is locked}}
+do_test lock3-4.3 {
+ execsql {END TRANSACTION}
+} {}
+
+catch {db2 close}
+
+finish_test
diff --git a/test/pager2.test b/test/pager2.test
index 31719728d6..097f0dcf62 100644
--- a/test/pager2.test
+++ b/test/pager2.test
@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The
# focus of this script is page cache subsystem.
#
-# $Id: pager2.test,v 1.3 2004/09/08 20:13:06 drh Exp $
+# $Id: pager2.test,v 1.4 2004/10/05 02:41:43 drh Exp $
set testdir [file dirname $argv0]
@@ -122,7 +122,7 @@ do_test pager2-2.14 {
} {0 {}}
do_test pager2-2.15 {
pager_stats $::p1
-} {ref 1 page 1 max 10 size 1 state 3 err 0 hit 1 miss 1 ovfl 0}
+} {ref 1 page 1 max 10 size 1 state 4 err 0 hit 1 miss 1 ovfl 0}
do_test pager2-2.16 {
page_read $::g1
} {Page-One}
@@ -295,7 +295,7 @@ for {set i 1} {$i<20} {incr i} {
do_test pager2-4.5.$i.1 {
page_write $g1 "Page-1 v$i"
lrange [pager_stats $p1] 8 9
- } {state 3}
+ } {state 4}
do_test pager2-4.5.$i.2 {
for {set j 2} {$j<=20} {incr j} {
set gx [page_get $p1 $j]
@@ -336,7 +336,7 @@ for {set i 1} {$i<20} {incr i} {
do_test pager2-4.5.$i.5 {
page_write $g1 "Page-1 v$i"
lrange [pager_stats $p1] 8 9
- } {state 3}
+ } {state 4}
do_test pager2-4.5.$i.6 {
for {set j 2} {$j<=20} {incr j} {
set gx [page_get $p1 $j]
diff --git a/www/lang.tcl b/www/lang.tcl
index 5f0e3f45df..3257a8d793 100644
--- a/www/lang.tcl
+++ b/www/lang.tcl
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
-set rcsid {$Id: lang.tcl,v 1.72 2004/09/08 13:06:21 drh Exp $}
+set rcsid {$Id: lang.tcl,v 1.73 2004/10/05 02:41:43 drh Exp $}
source common.tcl
header {Query Language Understood by SQLite}
puts {
@@ -169,7 +169,7 @@ the main database were ":memory:".
Section {BEGIN TRANSACTION} transaction
Syntax {sql-statement} {
-BEGIN [TRANSACTION []]
+BEGIN [ DEFERRED | IMMEDIATE | EXCLUSIVE ] [TRANSACTION []]
}
Syntax {sql-statement} {
END [TRANSACTION []]
@@ -207,6 +207,42 @@ clause for additional information about the ROLLBACK
conflict resolution algorithm.
+
+In SQLite version 3.0.8 and later, transactions can be deferred,
+immediate, or exclusive. Deferred means that no locks are acquired
+on the database until the database is first accessed. Thus with a
+deferred transaction, the BEGIN statement itself does nothing. Locks
+are not acquired until the first read or write operation. The first read
+operation against a database creates a SHARED lock and the first
+write operation creates a RESERVED lock. Because the acquisition of
+locks is deferred until they are needed, it is possible that another
+thread or process could create a separate transaction and write to
+the database after the BEGIN on the current thread has executed.
+If the transation is immediate, then RESERVED locks
+are acquired on all databases as soon as the BEGIN command is
+executed, without waiting for the
+database to be used. After a BEGIN IMMEDIATE, you are guaranteed that
+no other thread or process will be able to write to the database or
+do a BEGIN IMMEDIATE or BEGIN EXCLUSIVE. Other processes can continue
+to read from the database, however. An exclusive transaction causes
+EXCLUSIVE locks to be acquired on all databases. After a BEGIN
+EXCLUSIVE, you are guaranteed that no other thread or process will
+be able to read or write the database until the transaction is
+complete.
+
+
+
+A description of the meaning of SHARED, RESERVED, and EXCLUSIVE locks
+is available separately.
+
+
+
+The default behavior for SQLite version 3.0.8 is a
+deferred transaction. For SQLite version 3.0.0 through 3.0.7,
+deferred is the only kind of transaction available. For SQLite
+version 2.8 and earlier, all transactions are exclusive.
+
+
The COMMIT command does not actually perform a commit until all
pending SQL commands finish. Thus if two or more SELECT statements