diff options
author | drh <drh@noemail.net> | 2001-09-23 02:35:53 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2001-09-23 02:35:53 +0000 |
commit | ecdc7530dda7a793c96df4010313792de4a6390c (patch) | |
tree | eaef12ba2ead1e93fd05f62f34ba7cb0f0cf39cc | |
parent | beae319476c5d81e8f399010dc7dc5a2336f0606 (diff) | |
download | sqlite-ecdc7530dda7a793c96df4010313792de4a6390c.tar.gz sqlite-ecdc7530dda7a793c96df4010313792de4a6390c.zip |
Fixes to the locking and rollback behavior. (CVS 261)
FossilOrigin-Name: 337b3d3b2a903328d9744c111979909a284b8348
-rw-r--r-- | manifest | 42 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/btree.c | 82 | ||||
-rw-r--r-- | src/btree.h | 4 | ||||
-rw-r--r-- | src/build.c | 24 | ||||
-rw-r--r-- | src/delete.c | 19 | ||||
-rw-r--r-- | src/insert.c | 17 | ||||
-rw-r--r-- | src/sqlite.h.in | 29 | ||||
-rw-r--r-- | src/test3.c | 12 | ||||
-rw-r--r-- | src/tokenize.c | 5 | ||||
-rw-r--r-- | src/update.c | 17 | ||||
-rw-r--r-- | src/util.c | 5 | ||||
-rw-r--r-- | src/vdbe.c | 94 | ||||
-rw-r--r-- | src/vdbe.h | 223 | ||||
-rw-r--r-- | test/btree.test | 28 | ||||
-rw-r--r-- | test/btree2.test | 56 | ||||
-rw-r--r-- | test/lock.test | 190 | ||||
-rw-r--r-- | test/trans.test | 22 |
18 files changed, 518 insertions, 353 deletions
@@ -1,5 +1,5 @@ -C Put\sin\sa\sgeneric\shash\stable\ssystem\sin\splace\sof\sthe\svarious\sad-hoc\nhash\stable\sscattered\severywhere.\s\sExcept,\sthe\spage\shash\stable\sin\nthe\spager\sis\sunchanged.\s(CVS\s260) -D 2001-09-22T18:12:09 +C Fixes\sto\sthe\slocking\sand\srollback\sbehavior.\s(CVS\s261) +D 2001-09-23T02:35:53 F Makefile.in 18eea9a3486939fced70aa95b691be766c2c995d F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 F VERSION 6942aa44940d2972bd72f671a631060106e77f7e @@ -8,14 +8,14 @@ F configure.in 0000c0d62beb47cae1d2d81a197c7fe6efd56a45 F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538 F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6 -F src/btree.c 39da79b5a656870aa3ab72d40374fb38bd1bd12d -F src/btree.h fcb08daab59fd81023204ac71955233e218443c2 -F src/build.c 8af632a8024fa1132e4f40be48915083405614df -F src/delete.c ca7ca9bf8b613730821c4a755030d1a020b5e067 +F src/btree.c da9b60a0b94daac0b7a231f42cb79961bf06531d +F src/btree.h 5e5531869e53623aad5b32c22249c5743039251e +F src/build.c 8dbdcce4b9b9cb15b0d1a7a535af622cccba6f6b +F src/delete.c 81002d889aae874decf507627207c5d1b3599dc2 F src/expr.c 343a515a4abaf60e9e26c7412aa8c43fd3eae97d F src/hash.c bf36fb4cba114015123b0050f137d2c4553778a1 F src/hash.h 5f6e7c04c46ed015ab4e01797c2049b4af5b006d -F src/insert.c b34860ea58525754f18bde652f74161295ca2455 +F src/insert.c 061e531d19869e26ba9202c6d069385237b4c102 F src/main.c 49af06b7327c8b23b9331ce80b7e4bc9536ed2e1 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c F src/os.c faf9f484f3261c7650021cae79294338491f2cfb @@ -28,22 +28,22 @@ F src/random.c b6a57e85288470b013ad584a8813e893b60e62fe F src/select.c 7d90a6464906419fde96c0707a4cf4f3280db318 F src/shell.c 8e573138074e0b9526fca59b3eac22bdf18ecc03 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e -F src/sqlite.h.in 5d78c86bd9a9b8bbba65f860fbaf71c1882d6030 +F src/sqlite.h.in dbe7a1b1e1ab9bfce1a6983cfa6f53c5c2499305 F src/sqliteInt.h ae90beff6acc510bf98c80908d86b0830933e507 F src/table.c abd0adbe0fee39d995287b3bcccd908d174dfcac F src/tclsqlite.c 04a35d04f06046acc3944121dc6c36717f7f36d5 F src/test1.c 3892caa5372789a6d17329dd595724e1f06cb6de F src/test2.c 0168b39225b768cfdadd534406f9dec58c27879e -F src/test3.c f46bad555db7a6a25be332a96ac99e4d68a1b0c5 -F src/tokenize.c 2adf0568edf41b3d3c2fcb541ac49bd6e662da0c -F src/update.c a1952ad5d53379fa2b2d12efae5993ddb85a1ddb -F src/util.c 2a3491fd761b64cca849b07095076f482d119f9c -F src/vdbe.c 1cf36bea586e659995545ac8ad9534e794f4296f -F src/vdbe.h 900b59b46afdfb9c048a2a31a4478f380ab8504e +F src/test3.c 4a0d7b882fdae731dbb759f512ad867122452f96 +F src/tokenize.c 2ab07b85fde38d8fa2b4e73417b93e94f9cf8f5f +F src/update.c 8de22957017e17c5e751ba71c4ea76c60f93aa2f +F src/util.c 9c888445c1fd7896dab38fa62efc532f2364010a +F src/vdbe.c 7132265f449b4bb159e77cc548e515b7b8b9398a +F src/vdbe.h dc1d441494ba560a1ff464e1c56beb8ca03844fc F src/where.c cce952b6a2459ac2296e3432876a4252d2fe3b87 F test/all.test a2320eb40b462f25bd3e33115b1cabf3791450dd -F test/btree.test bb1d1caf834aa22a208ce6cc7d8d8bd0e106cd59 -F test/btree2.test ddc13a8de33461391da8403ded3e6b091f08dab4 +F test/btree.test 47952c7a0c22660566264c68c0664592b7da85ce +F test/btree2.test 20ce47ab804f15b6563736528bdd38aabe5193dc F test/copy.test 768e6f1701a07d08090e1ca7f7dcce0a7a72b43e F test/delete.test 5ebb114582457428b3e0e30b21b477fedcb85609 F test/expr.test b3475005ea19d53bf8c4573fb6e4a4498be5b434 @@ -52,7 +52,7 @@ F test/in.test 9323681388be301dc73f370b4cd62c5a33f79d1e F test/index.test e43e952b482c2afe938f1f31b71e2b33d43893a9 F test/insert.test a5c122aa726f1cef6f07d6767e8fd6f220994c11 F test/insert2.test 252d7130d8cc20f649b31a4f503cd87e660abda8 -F test/lock.test 5b4d969ab92c88f8dc10d1b870a2e5fe51ee7f5f +F test/lock.test 3cef6b302ae0826755ccb226fe444be42fe5d391 F test/main.test 085ece17913a487caacbc0a392638c958c83a75d F test/malloc.test f1400a8d002eb96f1ca0a34abe56d2ab3e324740 F test/misc1.test 50a5ca3481fc1f3cd6b978bcd6ed04c06f26a1e6 @@ -72,7 +72,7 @@ F test/table.test 52fdca1632580fb638c7b7dd14f4d37ecc09f994 F test/tableapi.test 162840153191a91a7dce6395f2334f9aef713b37 F test/tclsqlite.test a57bb478d7e9f0b2c927f92e161f391e2896631a F test/tester.tcl 957cd92fe8645b829da175d94b7ddb7ea68dac39 -F test/trans.test 997c8dcc15c479bc2cedc42220cf2d265e63d2a8 +F test/trans.test 010dfe3cc7dea8bfd3b389dcadc6789f35d6df36 F test/update.test b320ea22899e80b32b4d21c54591eb7a6ba4d6bd F test/vacuum.test 8acf8669f3b627e54149b25165b034aa06c2432e F test/where.test 43d5ac94da3f3722375307f948884dc79b326a91 @@ -97,7 +97,7 @@ F www/opcode.tcl 60222aeb57a7855b2582c374b8753cb5bb53c4ab F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f F www/tclsqlite.tcl 13d50723f583888fc80ae1a38247c0ab415066fa F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad -P 13afb22409b3b58d4c4b97a9fac22c96153d77c0 -R 453b3eb283d27c63c0996482bd747b07 +P 9114420dd01d92cc8890046500a8806a297a4e65 +R befae190f48e6ee61c8ee654bf44939d U drh -Z 39fe4015001e5b10c0870078a2610b25 +Z 7d651cd9fc4593916e1eb971a253a3a2 diff --git a/manifest.uuid b/manifest.uuid index 3b85bd851..f0500d753 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9114420dd01d92cc8890046500a8806a297a4e65
\ No newline at end of file +337b3d3b2a903328d9744c111979909a284b8348
\ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 09742e62c..909ead07d 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.29 2001/09/16 00:13:26 drh Exp $ +** $Id: btree.c,v 1.30 2001/09/23 02:35:53 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -312,6 +312,7 @@ struct Btree { BtCursor *pCursor; /* A list of all open cursors */ PageOne *page1; /* First page of the database */ int inTrans; /* True if a transaction is in progress */ + Hash locks; /* Key: root page number. Data: lock count */ }; typedef Btree Bt; @@ -326,6 +327,7 @@ struct BtCursor { Pgno pgnoRoot; /* The root page of this tree */ MemPage *pPage; /* Page that contains the entry */ int idx; /* Index of the entry in pPage->apCell[] */ + u8 wrFlag; /* True if writable */ u8 bSkipNext; /* sqliteBtreeNext() is no-op if true */ u8 iMatch; /* compare result from last sqliteBtreeMoveto() */ }; @@ -619,6 +621,7 @@ int sqliteBtreeOpen( sqlitepager_set_destructor(pBt->pPager, pageDestructor); pBt->pCursor = 0; pBt->page1 = 0; + sqliteHashInit(&pBt->locks, SQLITE_HASH_INT, 0); *ppBtree = pBt; return SQLITE_OK; } @@ -631,6 +634,7 @@ int sqliteBtreeClose(Btree *pBt){ sqliteBtreeCloseCursor(pBt->pCursor); } sqlitepager_close(pBt->pPager); + sqliteHashClear(&pBt->locks); sqliteFree(pBt); return SQLITE_OK; } @@ -771,17 +775,25 @@ int sqliteBtreeCommit(Btree *pBt){ } /* -** Rollback the transaction in progress. All cursors must be -** closed before this routine is called. +** Rollback the transaction in progress. All cursors will be +** invalided by this operation. Any attempt to use a cursor +** that was open at the beginning of this operation will result +** in an error. ** ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ int sqliteBtreeRollback(Btree *pBt){ int rc; - if( pBt->pCursor!=0 ) return SQLITE_ERROR; + BtCursor *pCur; if( pBt->inTrans==0 ) return SQLITE_OK; pBt->inTrans = 0; + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + if( pCur->pPage ){ + sqlitepager_unref(pCur->pPage); + pCur->pPage = 0; + } + } rc = sqlitepager_rollback(pBt->pPager); unlockBtreeIfUnused(pBt); return rc; @@ -792,9 +804,11 @@ int sqliteBtreeRollback(Btree *pBt){ ** iTable. The act of acquiring a cursor gets a read lock on ** the database file. */ -int sqliteBtreeCursor(Btree *pBt, int iTable, BtCursor **ppCur){ +int sqliteBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){ int rc; BtCursor *pCur; + int nLock; + if( pBt->page1==0 ){ rc = lockBtree(pBt); if( rc!=SQLITE_OK ){ @@ -816,7 +830,15 @@ int sqliteBtreeCursor(Btree *pBt, int iTable, BtCursor **ppCur){ if( rc!=SQLITE_OK ){ goto create_cursor_exception; } + nLock = (int)sqliteHashFind(&pBt->locks, 0, iTable); + if( nLock<0 || (nLock>0 && wrFlag) ){ + rc = SQLITE_LOCKED; + goto create_cursor_exception; + } + nLock = wrFlag ? -1 : nLock+1; + sqliteHashInsert(&pBt->locks, 0, iTable, (void*)nLock); pCur->pBt = pBt; + pCur->wrFlag = wrFlag; pCur->idx = 0; pCur->pNext = pBt->pCursor; if( pCur->pNext ){ @@ -842,6 +864,7 @@ create_cursor_exception: ** when the last cursor is closed. */ int sqliteBtreeCloseCursor(BtCursor *pCur){ + int nLock; Btree *pBt = pCur->pBt; if( pCur->pPrev ){ pCur->pPrev->pNext = pCur->pNext; @@ -851,8 +874,14 @@ int sqliteBtreeCloseCursor(BtCursor *pCur){ if( pCur->pNext ){ pCur->pNext->pPrev = pCur->pPrev; } - sqlitepager_unref(pCur->pPage); + if( pCur->pPage ){ + sqlitepager_unref(pCur->pPage); + } unlockBtreeIfUnused(pBt); + nLock = (int)sqliteHashFind(&pBt->locks, 0, pCur->pgnoRoot); + assert( nLock!=0 ); + nLock = nLock<0 ? 0 : nLock-1; + sqliteHashInsert(&pBt->locks, 0, pCur->pgnoRoot, (void*)nLock); sqliteFree(pCur); return SQLITE_OK; } @@ -865,7 +894,9 @@ static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){ memcpy(pTempCur, pCur, sizeof(*pCur)); pTempCur->pNext = 0; pTempCur->pPrev = 0; - sqlitepager_ref(pTempCur->pPage); + if( pTempCur->pPage ){ + sqlitepager_ref(pTempCur->pPage); + } } /* @@ -873,7 +904,9 @@ static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){ ** function above. */ static void releaseTempCursor(BtCursor *pCur){ - sqlitepager_unref(pCur->pPage); + if( pCur->pPage ){ + sqlitepager_unref(pCur->pPage); + } } /* @@ -888,8 +921,7 @@ int sqliteBtreeKeySize(BtCursor *pCur, int *pSize){ MemPage *pPage; pPage = pCur->pPage; - assert( pPage!=0 ); - if( pCur->idx >= pPage->nCell ){ + if( pPage==0 || pCur->idx >= pPage->nCell ){ *pSize = 0; }else{ pCell = pPage->apCell[pCur->idx]; @@ -971,7 +1003,7 @@ int sqliteBtreeKey(BtCursor *pCur, int offset, int amt, char *zBuf){ if( offset<0 ) return 0; if( amt==0 ) return 0; pPage = pCur->pPage; - assert( pPage!=0 ); + if( pPage==0 ) return 0; if( pCur->idx >= pPage->nCell ){ return 0; } @@ -998,8 +1030,7 @@ int sqliteBtreeDataSize(BtCursor *pCur, int *pSize){ MemPage *pPage; pPage = pCur->pPage; - assert( pPage!=0 ); - if( pCur->idx >= pPage->nCell ){ + if( pPage==0 || pCur->idx >= pPage->nCell ){ *pSize = 0; }else{ pCell = pPage->apCell[pCur->idx]; @@ -1024,8 +1055,7 @@ int sqliteBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){ if( offset<0 ) return 0; if( amt==0 ) return 0; pPage = pCur->pPage; - assert( pPage!=0 ); - if( pCur->idx >= pPage->nCell ){ + if( pPage==0 || pCur->idx >= pPage->nCell ){ return 0; } pCell = pPage->apCell[pCur->idx]; @@ -1190,6 +1220,7 @@ static int moveToLeftmost(BtCursor *pCur){ */ int sqliteBtreeFirst(BtCursor *pCur, int *pRes){ int rc; + if( pCur->pPage==0 ) return SQLITE_ABORT; rc = moveToRoot(pCur); if( rc ) return rc; if( pCur->pPage->nCell==0 ){ @@ -1225,6 +1256,7 @@ int sqliteBtreeFirst(BtCursor *pCur, int *pRes){ */ int sqliteBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){ int rc; + if( pCur->pPage==0 ) return SQLITE_ABORT; pCur->bSkipNext = 0; rc = moveToRoot(pCur); if( rc ) return rc; @@ -1275,6 +1307,9 @@ int sqliteBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){ */ int sqliteBtreeNext(BtCursor *pCur, int *pRes){ int rc; + if( pCur->pPage==0 ){ + return SQLITE_ABORT; + } if( pCur->bSkipNext ){ pCur->bSkipNext = 0; if( pRes ) *pRes = 0; @@ -2045,9 +2080,15 @@ int sqliteBtreeInsert( MemPage *pPage; Btree *pBt = pCur->pBt; + if( pCur->pPage==0 ){ + return SQLITE_ABORT; /* A rollback destroyed this cursor */ + } if( !pCur->pBt->inTrans || nKey+nData==0 ){ return SQLITE_ERROR; /* Must start a transaction first */ } + if( !pCur->wrFlag ){ + return SQLITE_PERM; /* Cursor not open for writing */ + } rc = sqliteBtreeMoveto(pCur, pKey, nKey, &loc); if( rc ) return rc; pPage = pCur->pPage; @@ -2090,12 +2131,18 @@ int sqliteBtreeDelete(BtCursor *pCur){ int rc; Pgno pgnoChild; + if( pCur->pPage==0 ){ + return SQLITE_ABORT; /* A rollback destroyed this cursor */ + } if( !pCur->pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } if( pCur->idx >= pPage->nCell ){ return SQLITE_ERROR; /* The cursor is not pointing to anything */ } + if( !pCur->wrFlag ){ + return SQLITE_PERM; /* Did not open this cursor for writing */ + } rc = sqlitepager_write(pPage); if( rc ) return rc; pCell = pPage->apCell[pCur->idx]; @@ -2207,9 +2254,14 @@ static int clearDatabasePage(Btree *pBt, Pgno pgno, int freePageFlag){ */ int sqliteBtreeClearTable(Btree *pBt, int iTable){ int rc; + int nLock; if( !pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } + nLock = (int)sqliteHashFind(&pBt->locks, 0, iTable); + if( nLock ){ + return SQLITE_LOCKED; + } rc = clearDatabasePage(pBt, (Pgno)iTable, 0); if( rc ){ sqliteBtreeRollback(pBt); diff --git a/src/btree.h b/src/btree.h index 02cd9b59e..4efd718e6 100644 --- a/src/btree.h +++ b/src/btree.h @@ -12,7 +12,7 @@ ** This header file defines the interface that the sqlite B-Tree file ** subsystem. ** -** @(#) $Id: btree.h,v 1.14 2001/09/16 00:13:26 drh Exp $ +** @(#) $Id: btree.h,v 1.15 2001/09/23 02:35:53 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -32,7 +32,7 @@ int sqliteBtreeCreateTable(Btree*, int*); int sqliteBtreeDropTable(Btree*, int); int sqliteBtreeClearTable(Btree*, int); -int sqliteBtreeCursor(Btree*, int iTable, BtCursor **ppCur); +int sqliteBtreeCursor(Btree*, int iTable, int wrFlag, BtCursor **ppCur); int sqliteBtreeMoveto(BtCursor*, const void *pKey, int nKey, int *pRes); int sqliteBtreeDelete(BtCursor*); int sqliteBtreeInsert(BtCursor*, const void *pKey, int nKey, diff --git a/src/build.c b/src/build.c index 2f05e368b..24862028c 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.38 2001/09/22 18:12:10 drh Exp $ +** $Id: build.c,v 1.39 2001/09/23 02:35:53 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -54,6 +54,7 @@ void sqliteExec(Parse *pParse){ rc = sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg, &pParse->zErrMsg, db->pBusyArg, db->xBusyCallback); + if( rc ) pParse->nErr++; } sqliteVdbeDelete(pParse->pVdbe); pParse->pVdbe = 0; @@ -372,6 +373,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ if( v ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); + pParse->schemaVerified = 1; } } } @@ -502,7 +504,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){ v = sqliteGetVdbe(pParse); if( v==0 ) return; n = (int)pEnd->z - (int)pParse->sFirstToken.z + 1; - sqliteVdbeAddOp(v, OP_Open, 0, 2, MASTER_NAME, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2, MASTER_NAME, 0); sqliteVdbeAddOp(v, OP_NewRecno, 0, 0, 0, 0); sqliteVdbeAddOp(v, OP_String, 0, 0, "table", 0); sqliteVdbeAddOp(v, OP_String, 0, 0, p->zName, 0); @@ -583,7 +585,7 @@ void sqliteDropTable(Parse *pParse, Token *pName){ v = sqliteGetVdbe(pParse); if( v ){ static VdbeOp dropTable[] = { - { OP_Open, 0, 2, MASTER_NAME}, + { OP_OpenWrite, 0, 2, MASTER_NAME}, { OP_Rewind, 0, 0, 0}, { OP_String, 0, 0, 0}, /* 2 */ { OP_Next, 0, ADDR(9), 0}, /* 3 */ @@ -600,6 +602,7 @@ void sqliteDropTable(Parse *pParse, Token *pName){ if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); + pParse->schemaVerified = 1; } base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); sqliteVdbeChangeP3(v, base+2, pTable->zName, 0); @@ -779,14 +782,14 @@ void sqliteCreateIndex( */ else if( pParse->initFlag==0 && pTable!=0 ){ static VdbeOp addTable[] = { - { OP_Open, 2, 2, MASTER_NAME}, + { OP_OpenWrite, 2, 2, MASTER_NAME}, { OP_NewRecno, 2, 0, 0}, { OP_String, 0, 0, "index"}, { OP_String, 0, 0, 0}, /* 3 */ { OP_String, 0, 0, 0}, /* 4 */ { OP_CreateIndex, 1, 0, 0}, { OP_Dup, 0, 0, 0}, - { OP_Open, 1, 0, 0}, /* 7 */ + { OP_OpenWrite, 1, 0, 0}, /* 7 */ { OP_Null, 0, 0, 0}, { OP_String, 0, 0, 0}, /* 9 */ { OP_MakeRecord, 6, 0, 0}, @@ -804,6 +807,7 @@ void sqliteCreateIndex( if( pTable!=0 && (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); + pParse->schemaVerified = 1; } if( pStart && pEnd ){ int base; @@ -875,7 +879,7 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ v = sqliteGetVdbe(pParse); if( v ){ static VdbeOp dropIndex[] = { - { OP_Open, 0, 2, MASTER_NAME}, + { OP_OpenWrite, 0, 2, MASTER_NAME}, { OP_Rewind, 0, 0, 0}, { OP_String, 0, 0, 0}, /* 2 */ { OP_Next, 0, ADDR(8), 0}, /* 3 */ @@ -892,6 +896,7 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); + pParse->schemaVerified = 1; } base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); sqliteVdbeChangeP3(v, base+2, pIndex->zName, 0); @@ -1065,13 +1070,14 @@ void sqliteCopy( if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); + pParse->schemaVerified = 1; } addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0, 0, 0); sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n); sqliteVdbeDequoteP3(v, addr); - sqliteVdbeAddOp(v, OP_Open, 0, pTab->tnum, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, 0, pTab->tnum, pTab->zName, 0); for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - sqliteVdbeAddOp(v, OP_Open, i, pIdx->tnum, pIdx->zName, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, i, pIdx->tnum, pIdx->zName, 0); } end = sqliteVdbeMakeLabel(v); addr = sqliteVdbeAddOp(v, OP_FileRead, pTab->nCol, end, 0, 0); @@ -1138,6 +1144,7 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){ if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); + pParse->schemaVerified = 1; } if( zName ){ sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, zName, 0); @@ -1176,6 +1183,7 @@ void sqliteBeginTransaction(Parse *pParse){ if( v ){ sqliteVdbeAddOp(v, OP_Transaction, 1, 0, 0, 0); sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); + pParse->schemaVerified = 1; } db->flags |= SQLITE_InTrans; } diff --git a/src/delete.c b/src/delete.c index 2b712307b..120295b5f 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.14 2001/09/16 00:13:27 drh Exp $ +** $Id: delete.c,v 1.15 2001/09/23 02:35:53 drh Exp $ */ #include "sqliteInt.h" @@ -32,11 +32,13 @@ void sqliteDeleteFrom( WhereInfo *pWInfo; /* Information about the WHERE clause */ Index *pIdx; /* For looping over indices of the table */ int base; /* Index of the first available table cursor */ + sqlite *db; /* Main database structure */ if( pParse->nErr || sqlite_malloc_failed ){ pTabList = 0; goto delete_from_cleanup; } + db = pParse->db; /* Locate the table which we want to delete. This table has to be ** put in an IdList structure because some of the subroutines we @@ -46,7 +48,7 @@ void sqliteDeleteFrom( pTabList = sqliteIdListAppend(0, pTableName); if( pTabList==0 ) goto delete_from_cleanup; for(i=0; i<pTabList->nId; i++){ - pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); + pTabList->a[i].pTab = sqliteFindTable(db, pTabList->a[i].zName); if( pTabList->a[i].pTab==0 ){ sqliteSetString(&pParse->zErrMsg, "no such table: ", pTabList->a[i].zName, 0); @@ -78,14 +80,15 @@ void sqliteDeleteFrom( */ v = sqliteGetVdbe(pParse); if( v==0 ) goto delete_from_cleanup; - if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); + pParse->schemaVerified = 1; } /* Special case: A DELETE without a WHERE clause deletes everything. - ** It is easier just to clear all information the database tables directly. + ** It is easier just to erase the whole table. */ if( pWhere==0 ){ sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, 0, 0, 0); @@ -118,9 +121,9 @@ void sqliteDeleteFrom( */ base = pParse->nTab; sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Open, base, pTab->tnum, 0, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum, 0, 0); for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, OP_Open, base+i, pIdx->tnum, 0, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, base+i, pIdx->tnum, 0, 0); } end = sqliteVdbeMakeLabel(v); addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0); @@ -140,7 +143,7 @@ void sqliteDeleteFrom( sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end); } - if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); } diff --git a/src/insert.c b/src/insert.c index 3fcb61e70..8de423f67 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.18 2001/09/16 00:13:27 drh Exp $ +** $Id: insert.c,v 1.19 2001/09/23 02:35:53 drh Exp $ */ #include "sqliteInt.h" @@ -47,14 +47,16 @@ void sqliteInsert( int nColumn; /* Number of columns in the data */ int base; /* First available cursor */ int iCont, iBreak; /* Beginning and end of the loop over srcTab */ + sqlite *db; /* The main database structure */ if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup; + db = pParse->db; /* Locate the table into which we will be inserting new information. */ zTab = sqliteTableNameFromToken(pTableName); if( zTab==0 ) goto insert_cleanup; - pTab = sqliteFindTable(pParse->db, zTab); + pTab = sqliteFindTable(db, zTab); sqliteFree(zTab); if( pTab==0 ){ sqliteSetNString(&pParse->zErrMsg, "no such table: ", 0, @@ -73,9 +75,10 @@ void sqliteInsert( */ v = sqliteGetVdbe(pParse); if( v==0 ) goto insert_cleanup; - if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); + pParse->schemaVerified = 1; } /* Figure out how many columns of data are supplied. If the data @@ -152,9 +155,9 @@ void sqliteInsert( ** all indices of that table. */ base = pParse->nTab; - sqliteVdbeAddOp(v, OP_Open, base, pTab->tnum, pTab->zName, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum, pTab->zName, 0); for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ - sqliteVdbeAddOp(v, OP_Open, idx+base, pIdx->tnum, pIdx->zName, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum, pIdx->zName, 0); } /* If the data source is a SELECT statement, then we have to create @@ -237,7 +240,7 @@ void sqliteInsert( sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0); sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, iBreak); } - if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 952983716..6d56498b5 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.18 2001/09/20 01:44:43 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.19 2001/09/23 02:35:53 drh Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -138,19 +138,20 @@ int sqlite_exec( #define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */ #define SQLITE_PERM 3 /* Access permission denied */ #define SQLITE_ABORT 4 /* Callback routine requested an abort */ -#define SQLITE_BUSY 5 /* One or more database files are locked */ -#define SQLITE_NOMEM 6 /* A malloc() failed */ -#define SQLITE_READONLY 7 /* Attempt to write a readonly database */ -#define SQLITE_INTERRUPT 8 /* Operation terminated by sqlite_interrupt() */ -#define SQLITE_IOERR 9 /* Some kind of disk I/O error occurred */ -#define SQLITE_CORRUPT 10 /* The database disk image is malformed */ -#define SQLITE_NOTFOUND 11 /* (Internal Only) Table or record not found */ -#define SQLITE_FULL 12 /* Insertion failed because database is full */ -#define SQLITE_CANTOPEN 13 /* Unable to open the database file */ -#define SQLITE_PROTOCOL 14 /* Database lock protocol error */ -#define SQLITE_EMPTY 15 /* (Internal Only) Database table is empty */ -#define SQLITE_SCHEMA 16 /* The database schema changed */ -#define SQLITE_TOOBIG 17 /* Too much data for one row of a table */ +#define SQLITE_BUSY 5 /* The database file is locked */ +#define SQLITE_LOCKED 6 /* A table in the database is locked */ +#define SQLITE_NOMEM 7 /* A malloc() failed */ +#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ +#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */ +#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ +#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ +#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */ +#define SQLITE_FULL 13 /* Insertion failed because database is full */ +#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ +#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ +#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */ +#define SQLITE_SCHEMA 17 /* The database schema changed */ +#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */ /* This function causes any pending database operation to abort and ** return at its earliest opportunity. This routine is typically diff --git a/src/test3.c b/src/test3.c index 9dd084105..978631159 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.11 2001/09/16 00:13:27 drh Exp $ +** $Id: test3.c,v 1.12 2001/09/23 02:35:53 drh Exp $ */ #include "sqliteInt.h" #include "pager.h" @@ -499,7 +499,7 @@ static int btree_sanity_check( } /* -** Usage: btree_cursor ID TABLENUM +** Usage: btree_cursor ID TABLENUM WRITEABLE ** ** Create a new cursor. Return the ID for the cursor. */ @@ -513,16 +513,18 @@ static int btree_cursor( int iTable; BtCursor *pCur; int rc; + int wrFlag; char zBuf[30]; - if( argc!=3 ){ + if( argc!=4 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " ID TABLENUM\"", 0); + " ID TABLENUM WRITEABLE\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR; if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; - rc = sqliteBtreeCursor(pBt, iTable, &pCur); + if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR; + rc = sqliteBtreeCursor(pBt, iTable, wrFlag, &pCur); if( rc ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; diff --git a/src/tokenize.c b/src/tokenize.c index be9711321..53ebbbf1a 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.22 2001/09/16 00:13:27 drh Exp $ +** $Id: tokenize.c,v 1.23 2001/09/23 02:35:53 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -353,6 +353,9 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){ nErr++; sqliteFree(pParse->zErrMsg); pParse->zErrMsg = 0; + }else if( pParse->rc!=SQLITE_OK ){ + sqliteSetString(pzErrMsg, sqliteErrStr(pParse->rc), 0); + nErr++; } break; } diff --git a/src/update.c b/src/update.c index 62c024118..fb67ac66b 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.14 2001/09/16 00:13:27 drh Exp $ +** $Id: update.c,v 1.15 2001/09/23 02:35:53 drh Exp $ */ #include "sqliteInt.h" @@ -34,12 +34,14 @@ void sqliteUpdate( Index *pIdx; /* For looping over indices */ int nIdx; /* Number of indices that need updating */ int base; /* Index of first available table cursor */ + sqlite *db; /* The database structure */ Index **apIdx = 0; /* An array of indices that need updating too */ int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup; + db = pParse->db; /* Locate the table which we want to update. This table has to be ** put in an IdList structure because some of the subroutines we @@ -49,7 +51,7 @@ void sqliteUpdate( pTabList = sqliteIdListAppend(0, pTableName); if( pTabList==0 ) goto update_cleanup; for(i=0; i<pTabList->nId; i++){ - pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); + pTabList->a[i].pTab = sqliteFindTable(db, pTabList->a[i].zName); if( pTabList->a[i].pTab==0 ){ sqliteSetString(&pParse->zErrMsg, "no such table: ", pTabList->a[i].zName, 0); @@ -132,9 +134,10 @@ void sqliteUpdate( */ v = sqliteGetVdbe(pParse); if( v==0 ) goto update_cleanup; - if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0, 0, 0); + sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); + pParse->schemaVerified = 1; } /* Begin the database scan @@ -156,9 +159,9 @@ void sqliteUpdate( */ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); base = pParse->nTab; - sqliteVdbeAddOp(v, OP_Open, base, pTab->tnum, 0, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum, 0, 0); for(i=0; i<nIdx; i++){ - sqliteVdbeAddOp(v, OP_Open, base+i+1, apIdx[i]->tnum, 0, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, base+i+1, apIdx[i]->tnum, 0, 0); } /* Loop over every record that needs updating. We have to load @@ -216,7 +219,7 @@ void sqliteUpdate( */ sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0); sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end); - if( (pParse->db->flags & SQLITE_InTrans)==0 ){ + if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); } diff --git a/src/util.c b/src/util.c index 02f9443e2..e12c0a9e9 100644 --- a/src/util.c +++ b/src/util.c @@ -14,7 +14,7 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.27 2001/09/19 13:22:40 drh Exp $ +** $Id: util.c,v 1.28 2001/09/23 02:35:53 drh Exp $ */ #include "sqliteInt.h" #include <stdarg.h> @@ -973,7 +973,8 @@ const char *sqliteErrStr(int rc){ case SQLITE_INTERNAL: z = "internal SQLite implementation flaw"; break; case SQLITE_PERM: z = "access permission denied"; break; case SQLITE_ABORT: z = "callback requested query abort"; break; - case SQLITE_BUSY: z = "database in use by another process"; break; + case SQLITE_BUSY: z = "database is locked"; break; + case SQLITE_LOCKED: z = "database table is locked"; break; case SQLITE_NOMEM: z = "out of memory"; break; case SQLITE_READONLY: z = "attempt to write a readonly database"; break; case SQLITE_INTERRUPT: z = "interrupted"; break; diff --git a/src/vdbe.c b/src/vdbe.c index 19fb9663c..88fcaa2a3 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -30,7 +30,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.73 2001/09/22 18:12:10 drh Exp $ +** $Id: vdbe.c,v 1.74 2001/09/23 02:35:53 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -676,6 +676,19 @@ static void cleanupCursor(Cursor *pCx){ } /* +** Close all cursors +*/ +static void closeAllCursors(Vdbe *p){ + int i; + for(i=0; i<p->nCursor; i++){ + cleanupCursor(&p->aCsr[i]); + } + sqliteFree(p->aCsr); + p->aCsr = 0; + p->nCursor = 0; +} + +/* ** Clean up the VM after execution. ** ** This routine will automatically close any cursors, lists, and/or @@ -686,12 +699,7 @@ static void Cleanup(Vdbe *p){ PopStack(p, p->tos+1); sqliteFree(p->azColName); p->azColName = 0; - for(i=0; i<p->nCursor; i++){ - cleanupCursor(&p->aCsr[i]); - } - sqliteFree(p->aCsr); - p->aCsr = 0; - p->nCursor = 0; + closeAllCursors(p); for(i=0; i<p->nMem; i++){ if( p->aMem[i].s.flags & STK_Dyn ){ sqliteFree(p->aMem[i].z); @@ -777,30 +785,30 @@ void sqliteVdbeDelete(Vdbe *p){ static char *zOpName[] = { 0, "Transaction", "Commit", "Rollback", "ReadCookie", "SetCookie", "VerifyCookie", "Open", "OpenTemp", - "Close", "MoveTo", "Fcnt", "NewRecno", - "Put", "Distinct", "Found", "NotFound", - "Delete", "Column", "KeyAsData", "Recno", - "FullKey", "Rewind", "Next", "Destroy", - "Clear", "CreateIndex", "CreateTable", "Reorganize", - "BeginIdx", "NextIdx", "PutIdx", "DeleteIdx", - "MemLoad", "MemStore", "ListOpen", "ListWrite", - "ListRewind", "ListRead", "ListClose", "SortOpen", - "SortPut", "SortMakeRec", "SortMakeKey", "Sort", - "SortNext", "SortKey", "SortCallback", "SortClose", - "FileOpen", "FileRead", "FileColumn", "FileClose", - "AggReset", "AggFocus", "AggIncr", "AggNext", - "AggSet", "AggGet", "SetInsert", "SetFound", - "SetNotFound", "SetClear", "MakeRecord", "MakeKey", - "MakeIdxKey", "Goto", "If", "Halt", - "ColumnCount", "ColumnName", "Callback", "Integer", - "String", "Null", "Pop", "Dup", - "Pull", "Add", "AddImm", "Subtract", - "Multiply", "Divide", "Min", "Max", - "Like", "Glob", "Eq", "Ne", - "Lt", "Le", "Gt", "Ge", - "IsNull", "NotNull", "Negative", "And", - "Or", "Not", "Concat", "Noop", - "Strlen", "Substr", + "OpenWrite", "Close", "MoveTo", "Fcnt", + "NewRecno", "Put", "Distinct", "Found", + "NotFound", "Delete", "Column", "KeyAsData", + "Recno", "FullKey", "Rewind", "Next", + "Destroy", "Clear", "CreateIndex", "CreateTable", + "Reorganize", "BeginIdx", "NextIdx", "PutIdx", + "DeleteIdx", "MemLoad", "MemStore", "ListOpen", + "ListWrite", "ListRewind", "ListRead", "ListClose", + "SortOpen", "SortPut", "SortMakeRec", "SortMakeKey", + "Sort", "SortNext", "SortKey", "SortCallback", + "SortClose", "FileOpen", "FileRead", "FileColumn", + "FileClose", "AggReset", "AggFocus", "AggIncr", + "AggNext", "AggSet", "AggGet", "SetInsert", + "SetFound", "SetNotFound", "SetClear", "MakeRecord", + "MakeKey", "MakeIdxKey", "Goto", "If", + "Halt", "ColumnCount", "ColumnName", "Callback", + "Integer", "String", "Null", "Pop", + "Dup", "Pull", "Add", "AddImm", + "Subtract", "Multiply", "Divide", "Min", + "Max", "Like", "Glob", "Eq", + "Ne", "Lt", "Le", "Gt", + "Ge", "IsNull", "NotNull", "Negative", + "And", "Or", "Not", "Concat", + "Noop", "Strlen", "Substr", }; /* @@ -1985,7 +1993,7 @@ case OP_VerifyCookie: { /* Opcode: Open P1 P2 P3 ** -** Open a new cursor for the database table whose root page is +** Open a read-only cursor for the database table whose root page is ** P2 in the main database file. Give the new cursor an identifier ** of P1. The P1 values need not be contiguous but all P1 values ** should be small integers. It is an error for P1 to be negative. @@ -2006,6 +2014,16 @@ case OP_VerifyCookie: { ** omitted. But the code generator usually inserts the index or ** table name into P3 to make the code easier to read. */ +/* Opcode: OpenWrite P1 P2 P3 +** +** Open a read/write cursor named P1 on the table or index whose root +** page is P2. If P2==0 then take the root page number from the stack. +** +** This instruction works just like Open except that it opens the cursor +** in read/write mode. For a given table, there can be one or more read-only +** cursors or a single read/write cursor but not both. +*/ +case OP_OpenWrite: case OP_Open: { int busy = 0; int i = pOp->p1; @@ -2035,7 +2053,8 @@ case OP_Open: { cleanupCursor(&p->aCsr[i]); memset(&p->aCsr[i], 0, sizeof(Cursor)); do{ - rc = sqliteBtreeCursor(pBt, p2, &p->aCsr[i].pCursor); + int wrFlag = pOp->opcode==OP_OpenWrite; + rc = sqliteBtreeCursor(pBt, p2, wrFlag, &p->aCsr[i].pCursor); switch( rc ){ case SQLITE_BUSY: { if( xBusy==0 || (*xBusy)(pBusyArg, pOp->p3, ++busy)==0 ){ @@ -2081,7 +2100,7 @@ case OP_OpenTemp: { memset(pCx, 0, sizeof(*pCx)); rc = sqliteBtreeOpen(0, 0, TEMP_PAGES, &pCx->pBt); if( rc==SQLITE_OK ){ - rc = sqliteBtreeCursor(pCx->pBt, 2, &pCx->pCursor); + rc = sqliteBtreeCursor(pCx->pBt, 2, 1, &pCx->pCursor); } if( rc==SQLITE_OK ){ rc = sqliteBtreeBeginTrans(pCx->pBt); @@ -2637,7 +2656,7 @@ case OP_PutIdx: { BtCursor *pCrsr; VERIFY( if( tos<0 ) goto not_enough_stack; ) if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ - sqliteBtreeInsert(pCrsr, zStack[tos], aStack[tos].n, "", 0); + rc = sqliteBtreeInsert(pCrsr, zStack[tos], aStack[tos].n, "", 0); } POPSTACK; break; @@ -2657,7 +2676,7 @@ case OP_DeleteIdx: { int rx, res; rx = sqliteBtreeMoveto(pCrsr, zStack[tos], aStack[tos].n, &res); if( rx==SQLITE_OK && res==0 ){ - sqliteBtreeDelete(pCrsr); + rc = sqliteBtreeDelete(pCrsr); } } POPSTACK; @@ -3784,7 +3803,8 @@ cleanup: rc = SQLITE_INTERNAL; sqliteSetString(pzErrMsg, "table or index root page not set", 0); } - if( rc!=SQLITE_OK && (db->flags & SQLITE_InTrans)!=0 ){ + if( rc!=SQLITE_OK ){ + closeAllCursors(p); sqliteBtreeRollback(pBt); sqliteRollbackInternalChanges(db); db->flags &= ~SQLITE_InTrans; diff --git a/src/vdbe.h b/src/vdbe.h index b5634982a..c1975bd17 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,7 +15,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.23 2001/09/16 00:13:27 drh Exp $ +** $Id: vdbe.h,v 1.24 2001/09/23 02:35:53 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -69,116 +69,117 @@ typedef struct VdbeOp VdbeOp; #define OP_Open 7 #define OP_OpenTemp 8 -#define OP_Close 9 -#define OP_MoveTo 10 -#define OP_Fcnt 11 -#define OP_NewRecno 12 -#define OP_Put 13 -#define OP_Distinct 14 -#define OP_Found 15 -#define OP_NotFound 16 -#define OP_Delete 17 -#define OP_Column 18 -#define OP_KeyAsData 19 -#define OP_Recno 20 -#define OP_FullKey 21 -#define OP_Rewind 22 -#define OP_Next 23 - -#define OP_Destroy 24 -#define OP_Clear 25 -#define OP_CreateIndex 26 -#define OP_CreateTable 27 -#define OP_Reorganize 28 - -#define OP_BeginIdx 29 -#define OP_NextIdx 30 -#define OP_PutIdx 31 -#define OP_DeleteIdx 32 - -#define OP_MemLoad 33 -#define OP_MemStore 34 - -#define OP_ListOpen 35 -#define OP_ListWrite 36 -#define OP_ListRewind 37 -#define OP_ListRead 38 -#define OP_ListClose 39 - -#define OP_SortOpen 40 -#define OP_SortPut 41 -#define OP_SortMakeRec 42 -#define OP_SortMakeKey 43 -#define OP_Sort 44 -#define OP_SortNext 45 -#define OP_SortKey 46 -#define OP_SortCallback 47 -#define OP_SortClose 48 - -#define OP_FileOpen 49 -#define OP_FileRead 50 -#define OP_FileColumn 51 -#define OP_FileClose 52 - -#define OP_AggReset 53 -#define OP_AggFocus 54 -#define OP_AggIncr 55 -#define OP_AggNext 56 -#define OP_AggSet 57 -#define OP_AggGet 58 - -#define OP_SetInsert 59 -#define OP_SetFound 60 -#define OP_SetNotFound 61 -#define OP_SetClear 62 - -#define OP_MakeRecord 63 -#define OP_MakeKey 64 -#define OP_MakeIdxKey 65 - -#define OP_Goto 66 -#define OP_If 67 -#define OP_Halt 68 - -#define OP_ColumnCount 69 -#define OP_ColumnName 70 -#define OP_Callback 71 - -#define OP_Integer 72 -#define OP_String 73 -#define OP_Null 74 -#define OP_Pop 75 -#define OP_Dup 76 -#define OP_Pull 77 - -#define OP_Add 78 -#define OP_AddImm 79 -#define OP_Subtract 80 -#define OP_Multiply 81 -#define OP_Divide 82 -#define OP_Min 83 -#define OP_Max 84 -#define OP_Like 85 -#define OP_Glob 86 -#define OP_Eq 87 -#define OP_Ne 88 -#define OP_Lt 89 -#define OP_Le 90 -#define OP_Gt 91 -#define OP_Ge 92 -#define OP_IsNull 93 -#define OP_NotNull 94 -#define OP_Negative 95 -#define OP_And 96 -#define OP_Or 97 -#define OP_Not 98 -#define OP_Concat 99 -#define OP_Noop 100 - -#define OP_Strlen 101 -#define OP_Substr 102 - -#define OP_MAX 102 +#define OP_OpenWrite 9 +#define OP_Close 10 +#define OP_MoveTo 11 +#define OP_Fcnt 12 +#define OP_NewRecno 13 +#define OP_Put 14 +#define OP_Distinct 15 +#define OP_Found 16 +#define OP_NotFound 17 +#define OP_Delete 18 +#define OP_Column 19 +#define OP_KeyAsData 20 +#define OP_Recno 21 +#define OP_FullKey 22 +#define OP_Rewind 23 +#define OP_Next 24 + +#define OP_Destroy 25 +#define OP_Clear 26 +#define OP_CreateIndex 27 +#define OP_CreateTable 28 +#define OP_Reorganize 29 + +#define OP_BeginIdx 30 +#define OP_NextIdx 31 +#define OP_PutIdx 32 +#define OP_DeleteIdx 33 + +#define OP_MemLoad 34 +#define OP_MemStore 35 + +#define OP_ListOpen 36 +#define OP_ListWrite 37 +#define OP_ListRewind 38 +#define OP_ListRead 39 +#define OP_ListClose 40 + +#define OP_SortOpen 41 +#define OP_SortPut 42 +#define OP_SortMakeRec 43 +#define OP_SortMakeKey 44 +#define OP_Sort 45 +#define OP_SortNext 46 +#define OP_SortKey 47 +#define OP_SortCallback 48 +#define OP_SortClose 49 + +#define OP_FileOpen 50 +#define OP_FileRead 51 +#define OP_FileColumn 52 +#define OP_FileClose 53 + +#define OP_AggReset 54 +#define OP_AggFocus 55 +#define OP_AggIncr 56 +#define OP_AggNext 57 +#define OP_AggSet 58 +#define OP_AggGet 59 + +#define OP_SetInsert 60 +#define OP_SetFound 61 +#define OP_SetNotFound 62 +#define OP_SetClear 63 + +#define OP_MakeRecord 64 +#define OP_MakeKey 65 +#define OP_MakeIdxKey 66 + +#define OP_Goto 67 +#define OP_If 68 +#define OP_Halt 69 + +#define OP_ColumnCount 70 +#define OP_ColumnName 71 +#define OP_Callback 72 + +#define OP_Integer 73 +#define OP_String 74 +#define OP_Null 75 +#define OP_Pop 76 +#define OP_Dup 77 +#define OP_Pull 78 + +#define OP_Add 79 +#define OP_AddImm 80 +#define OP_Subtract 81 +#define OP_Multiply 82 +#define OP_Divide 83 +#define OP_Min 84 +#define OP_Max 85 +#define OP_Like 86 +#define OP_Glob 87 +#define OP_Eq 88 +#define OP_Ne 89 +#define OP_Lt 90 +#define OP_Le 91 +#define OP_Gt 92 +#define OP_Ge 93 +#define OP_IsNull 94 +#define OP_NotNull 95 +#define OP_Negative 96 +#define OP_And 97 +#define OP_Or 98 +#define OP_Not 99 +#define OP_Concat 100 +#define OP_Noop 101 + +#define OP_Strlen 102 +#define OP_Substr 103 + +#define OP_MAX 103 /* ** Prototypes for the VDBE interface. See comments on the implementation diff --git a/test/btree.test b/test/btree.test index 8ba55a403..97e7847f6 100644 --- a/test/btree.test +++ b/test/btree.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is btree database backend # -# $Id: btree.test,v 1.9 2001/09/16 00:13:28 drh Exp $ +# $Id: btree.test,v 1.10 2001/09/23 02:35:53 drh Exp $ set testdir [file dirname $argv0] @@ -54,7 +54,7 @@ do_test btree-1.4.1 { lindex [btree_pager_stats $::b1] 1 } {1} do_test btree-1.5 { - set rc [catch {btree_cursor $::b1 2} ::c1] + set rc [catch {btree_cursor $::b1 2 1} ::c1] if {$rc} {lappend rc $::c1} set rc } {0} @@ -86,7 +86,7 @@ do_test btree-1.12 { # Reopen the database and attempt to read the record that we wrote. # do_test btree-2.1 { - set rc [catch {btree_cursor $::b1 2} ::c1] + set rc [catch {btree_cursor $::b1 2 1} ::c1] if {$rc} {lappend rc $::c1} set rc } {0} @@ -209,7 +209,7 @@ do_test btree-3.24 { file size test1.bt } {2048} do_test btree-3.25 { - set rc [catch {btree_cursor $::b1 2} ::c1] + set rc [catch {btree_cursor $::b1 2 1} ::c1] if {$rc} {lappend rc $::c1} set rc } {0} @@ -339,7 +339,7 @@ do_test btree-4.7 { do_test btree-4.8 { btree_close $::b1 set ::b1 [btree_open test1.bt] - set ::c1 [btree_cursor $::b1 2] + set ::c1 [btree_cursor $::b1 2 1] lindex [btree_pager_stats $::b1] 1 } {2} do_test btree-4.9 { @@ -421,7 +421,7 @@ do_test btree-6.2.1 { lindex [btree_pager_stats $::b1] 1 } {1} do_test btree-6.2.2 { - set ::c2 [btree_cursor $::b1 $::t2] + set ::c2 [btree_cursor $::b1 $::t2 1] lindex [btree_pager_stats $::b1] 1 } {2} do_test btree-6.2.3 { @@ -430,7 +430,7 @@ do_test btree-6.2.3 { } {ten} do_test btree-6.3 { btree_commit $::b1 - set ::c1 [btree_cursor $::b1 2] + set ::c1 [btree_cursor $::b1 2 1] lindex [btree_pager_stats $::b1] 1 } {3} do_test btree-6.3.1 { @@ -465,7 +465,7 @@ do_test btree-6.8.1 { lindex [btree_get_meta $::b1] 0 } {0} do_test btree-6.9 { - set ::c2 [btree_cursor $::b1 $::t2] + set ::c2 [btree_cursor $::b1 $::t2 1] lindex [btree_pager_stats $::b1] 1 } {3} @@ -479,7 +479,7 @@ do_test btree-6.9.1 { do_test btree-6.10 { btree_close_cursor $::c1 btree_drop_table $::b1 2 - set ::c1 [btree_cursor $::b1 2] + set ::c1 [btree_cursor $::b1 2 1] btree_move_to $::c1 {} btree_key $::c1 } {} @@ -631,7 +631,7 @@ do_test btree-8.9 { btree_close_cursor $::c1 btree_close $::b1 set ::b1 [btree_open test1.bt] - set ::c1 [btree_cursor $::b1 2] + set ::c1 [btree_cursor $::b1 2 1] btree_move_to $::c1 020 btree_data $::c1 } $::data @@ -653,7 +653,7 @@ do_test btree-8.12 { lindex [btree_get_meta $::b1] 0 } {4} do_test btree-8.12.1 { - set ::c1 [btree_cursor $::b1 2] + set ::c1 [btree_cursor $::b1 2 1] btree_insert $::c1 ${::keyprefix}1 1 btree_data $::c1 } {1} @@ -700,7 +700,7 @@ do_test btree-8.22 { do_test btree-8.23 { btree_close_cursor $::c1 btree_drop_table $::b1 2 - set ::c1 [btree_cursor $::b1 2] + set ::c1 [btree_cursor $::b1 2 1] lindex [btree_get_meta $::b1] 0 } {4} do_test btree-8.24 { @@ -790,7 +790,7 @@ do_test btree-10.1 { lindex [btree_pager_stats $::b1] 1 } {1} do_test btree-10.2 { - set ::c1 [btree_cursor $::b1 2] + set ::c1 [btree_cursor $::b1 2 1] lindex [btree_pager_stats $::b1] 1 } {2} do_test btree-10.3 { @@ -851,7 +851,7 @@ do_test btree-11.2 { lindex [btree_pager_stats $::b1] 1 } {1} do_test btree-11.3 { - set ::c1 [btree_cursor $::b1 2] + set ::c1 [btree_cursor $::b1 2 1] lindex [btree_pager_stats $::b1] 1 } {2} #btree_page_dump $::b1 2 diff --git a/test/btree2.test b/test/btree2.test index 7b594caf8..865d6b8bc 100644 --- a/test/btree2.test +++ b/test/btree2.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is btree database backend # -# $Id: btree2.test,v 1.7 2001/09/16 00:13:28 drh Exp $ +# $Id: btree2.test,v 1.8 2001/09/23 02:35:53 drh Exp $ set testdir [file dirname $argv0] @@ -48,7 +48,7 @@ do_test btree2-1.4 { btree_create_table $::b } {6} do_test btree2-1.5 { - set ::c2 [btree_cursor $::b 2] + set ::c2 [btree_cursor $::b 2 1] btree_insert $::c2 {one} {1} btree_delete $::c2 btree_close_cursor $::c2 @@ -95,7 +95,7 @@ proc build_db {N L} { for {set i 2} {$i<=6} {incr i} { catch {btree_close_cursor [set ::c$i]} btree_clear_table $::b $i - set ::c$i [btree_cursor $::b $i] + set ::c$i [btree_cursor $::b $i 1] } btree_insert $::c2 N $N btree_insert $::c2 L $L @@ -281,11 +281,11 @@ foreach {N L} { puts "**** N=$N L=$L ****" set hash [md5file test2.bt] do_test btree2-$testno.1 [subst -nocommands { - set ::c2 [btree_cursor $::b 2] - set ::c3 [btree_cursor $::b 3] - set ::c4 [btree_cursor $::b 4] - set ::c5 [btree_cursor $::b 5] - set ::c6 [btree_cursor $::b 6] + set ::c2 [btree_cursor $::b 2 1] + set ::c3 [btree_cursor $::b 3 1] + set ::c4 [btree_cursor $::b 4 1] + set ::c5 [btree_cursor $::b 5 1] + set ::c6 [btree_cursor $::b 6 1] btree_begin_transaction $::b build_db $N $L check_invariants @@ -301,11 +301,11 @@ foreach {N L} { } $hash do_test btree2-$testno.3 [subst -nocommands { btree_begin_transaction $::b - set ::c2 [btree_cursor $::b 2] - set ::c3 [btree_cursor $::b 3] - set ::c4 [btree_cursor $::b 4] - set ::c5 [btree_cursor $::b 5] - set ::c6 [btree_cursor $::b 6] + set ::c2 [btree_cursor $::b 2 1] + set ::c3 [btree_cursor $::b 3 1] + set ::c4 [btree_cursor $::b 4 1] + set ::c5 [btree_cursor $::b 5 1] + set ::c6 [btree_cursor $::b 6 1] build_db $N $L check_invariants }] {} @@ -327,11 +327,11 @@ foreach {N L} { do_test btree2-$testno.7 { btree_close $::b set ::b [btree_open test2.bt] - set ::c2 [btree_cursor $::b 2] - set ::c3 [btree_cursor $::b 3] - set ::c4 [btree_cursor $::b 4] - set ::c5 [btree_cursor $::b 5] - set ::c6 [btree_cursor $::b 6] + set ::c2 [btree_cursor $::b 2 1] + set ::c3 [btree_cursor $::b 3 1] + set ::c4 [btree_cursor $::b 4 1] + set ::c5 [btree_cursor $::b 5 1] + set ::c6 [btree_cursor $::b 6 1] check_invariants } {} @@ -375,11 +375,11 @@ foreach {N L} { } $hash # exec cp test2.bt test2.bt.bu2 btree_begin_transaction $::b - set ::c2 [btree_cursor $::b 2] - set ::c3 [btree_cursor $::b 3] - set ::c4 [btree_cursor $::b 4] - set ::c5 [btree_cursor $::b 5] - set ::c6 [btree_cursor $::b 6] + set ::c2 [btree_cursor $::b 2 1] + set ::c3 [btree_cursor $::b 3 1] + set ::c4 [btree_cursor $::b 4 1] + set ::c5 [btree_cursor $::b 5 1] + set ::c6 [btree_cursor $::b 6 1] do_test $testid.5 [subst { random_changes $n $I $K $D }] {} @@ -402,11 +402,11 @@ foreach {N L} { do_test $testid.9 { btree_close $::b set ::b [btree_open test2.bt] - set ::c2 [btree_cursor $::b 2] - set ::c3 [btree_cursor $::b 3] - set ::c4 [btree_cursor $::b 4] - set ::c5 [btree_cursor $::b 5] - set ::c6 [btree_cursor $::b 6] + set ::c2 [btree_cursor $::b 2 1] + set ::c3 [btree_cursor $::b 3 1] + set ::c4 [btree_cursor $::b 4 1] + set ::c5 [btree_cursor $::b 5 1] + set ::c6 [btree_cursor $::b 6 1] check_invariants } {} incr num2 diff --git a/test/lock.test b/test/lock.test index c6eaf0bf8..b5d546a1e 100644 --- a/test/lock.test +++ b/test/lock.test @@ -11,82 +11,150 @@ # This file implements regression tests for SQLite library. The # focus of this script is database locks. # -# $Id: lock.test,v 1.10 2001/09/16 00:13:28 drh Exp $ +# $Id: lock.test,v 1.11 2001/09/23 02:35:53 drh Exp $ -if {0} { set testdir [file dirname $argv0] source $testdir/tester.tcl -# Create a largish table +# Create an alternative connection to the database # do_test lock-1.0 { - execsql {CREATE TABLE big(f1 int, f2 int, f3 int)} - set f [open ./testdata1.txt w] - for {set i 1} {$i<=500} {incr i} { - puts $f "$i\t[expr {$i*2}]\t[expr {$i*3}]" - } - close $f - execsql {COPY big FROM './testdata1.txt'} - file delete -force ./testdata1.txt + sqlite db2 ./test.db } {} - do_test lock-1.1 { - # Create a background query that gives us a read lock on the big table - # - set f [open slow.sql w] - puts $f "SELECT a.f1, b.f1 FROM big AS a, big AS B" - puts $f "WHERE a.f1+b.f1==0.5;" - close $f - set ::lock_pid [exec ./sqlite testdb <slow.sql &] - after 250 - set v {} + execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name} +} {} +do_test lock-1.2 { + execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name} db2 } {} +do_test lock-1.3 { + execsql {CREATE TABLE t1(a int, b int)} + execsql {SELECT name FROM sqlite_master WHERE type='table' ORDER BY name} +} {t1} +do_test lock-1.4 { + set r [catch {execsql { + SELECT name FROM sqlite_master WHERE type='table' ORDER BY name + } db2} msg] + lappend r $msg +} {1 {database schema has changed}} +do_test lock-1.5 { + set r [catch {execsql { + SELECT name FROM sqlite_master WHERE type='table' ORDER BY name + } db2} msg] + lappend r $msg +} {0 t1} -do_probtest lock-1.2 { - # Now try to update the database - # - set v [catch {execsql {UPDATE big SET f2='xyz' WHERE f1=11}} msg] - lappend v $msg -} {1 {table big is locked}} +do_test lock-1.6 { + execsql {INSERT INTO t1 VALUES(1,2)} + execsql {SELECT * FROM t1} +} {1 2} +do_test lock-1.7 { + execsql {SELECT * FROM t1} db2 +} {1 2} +do_test lock-1.8 { + execsql {UPDATE t1 SET a=b, b=a} db2 + execsql {SELECT * FROM t1} db2 +} {2 1} +do_test lock-1.9 { + execsql {SELECT * FROM t1} +} {2 1} -do_probtest lock-1.3 { - # Try to update the database in a separate process - # - set f [open update.sql w] - puts $f ".timeout 0" - puts $f "UPDATE big SET f2='xyz' WHERE f1=11;" - puts $f "SELECT f2 FROM big WHERE f1=11;" - close $f - exec ./sqlite testdb <update.sql -} "UPDATE big SET f2='xyz' WHERE f1=11;\nSQL error: table big is locked\n22" +do_test lock-1.10 { + execsql {BEGIN TRANSACTION} + execsql {SELECT * FROM t1} +} {2 1} +do_test lock-1.11 { + set r [catch {execsql {SELECT * FROM t1} db2} msg] + lappend r $msg +} {1 {database is locked}} +do_test lock-1.12 { + execsql {ROLLBACK} + set r [catch {execsql {SELECT * FROM t1} db2} msg] + lappend r $msg +} {0 {2 1}} -do_probtest lock-1.4 { - # Try to update the database using a timeout - # - set f [open update.sql w] - puts $f ".timeout 1000" - puts $f "UPDATE big SET f2='xyz' WHERE f1=11;" - puts $f "SELECT f2 FROM big WHERE f1=11;" - close $f - exec ./sqlite testdb <update.sql -} "UPDATE big SET f2='xyz' WHERE f1=11;\nSQL error: table big is locked\n22" +do_test lock-1.13 { + execsql {CREATE TABLE t2(x int, y int)} + execsql {INSERT INTO t2 VALUES(8,9)} + execsql {SELECT * FROM t2} +} {8 9} +do_test lock-1.14 { + set r [catch {execsql {SELECT * FROM t1} db2} msg] + lappend r $msg +} {1 {database schema has changed}} +do_test lock-1.15 { + set r [catch {execsql {SELECT * FROM t2} db2} msg] + lappend r $msg +} {0 {8 9}} -do_probtest lock-1.5 { - # Try to update the database using a timeout - # - set f [open update.sql w] - puts $f ".timeout 10000" - puts $f "UPDATE big SET f2='xyz' WHERE f1=11;" - puts $f "SELECT f2 FROM big WHERE f1=11;" - close $f - exec ./sqlite testdb <update.sql -} {xyz} +do_test lock-1.16 { + db eval {SELECT * FROM t1} qv { + set x [db eval {SELECT * FROM t1}] + } + set x +} {2 1} +do_test lock-1.17 { + db eval {SELECT * FROM t1} qv { + set x [db eval {SELECT * FROM t2}] + } + set x +} {8 9} -catch {exec ps -uax | grep $::lock_pid} -catch {exec kill -HUP $::lock_pid} -catch {exec kill -9 $::lock_pid} +# You cannot UPDATE a table from within the callback of a SELECT
+# on that same table because the SELECT has the table locked. +# +do_test lock-1.18 { + db eval {SELECT * FROM t1} qv { + set r [catch {db eval {UPDATE t1 SET a=b, b=a}} msg] + lappend r $msg + } + set r +} {1 {database table is locked}} -finish_test +# But you can UPDATE a different table from the one that is used in +# the SELECT. +# +do_test lock-1.19 { + db eval {SELECT * FROM t1} qv { + set r [catch {db eval {UPDATE t2 SET x=y, y=x}} msg] + lappend r $msg + } + set r +} {0 {}} +do_test lock-1.20 { + execsql {SELECT * FROM t2} +} {9 8} +# It is possible to do a SELECT of the same table within the +# callback of another SELECT on that same table because two +# or more read-only cursors can be open at once. +# +do_test lock-1.21 { + db eval {SELECT * FROM t1} qv { + set r [catch {db eval {SELECT a FROM t1}} msg] + lappend r $msg + } + set r +} {0 2} + +# Under UNIX you can do two SELECTs at once with different database +# connections, because UNIX supports reader/writer locks. Under windows, +# this is not possible. +# +if {$::tcl_platform(platform)=="unix"} { + do_test lock-1.22 { + db eval {SELECT * FROM t1} qv { + set r [catch {db2 eval {SELECT a FROM t1}} msg] + lappend r $msg + } + set r + } {0 2} } + + +do_test lock-999.1 { + rename db2 {} +} {} + +finish_test diff --git a/test/trans.test b/test/trans.test index 1566b1540..8c2c6f81e 100644 --- a/test/trans.test +++ b/test/trans.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is database locks. # -# $Id: trans.test,v 1.5 2001/09/17 20:48:30 drh Exp $ +# $Id: trans.test,v 1.6 2001/09/23 02:35:53 drh Exp $ set testdir [file dirname $argv0] @@ -94,13 +94,13 @@ do_test trans-3.2 { SELECT a FROM two ORDER BY a; } altdb} msg] lappend v $msg -} {1 {database in use by another process}} +} {1 {database is locked}} do_test trans-3.3 { set v [catch {execsql { SELECT a FROM one ORDER BY a; } altdb} msg] lappend v $msg -} {1 {database in use by another process}} +} {1 {database is locked}} do_test trans-3.4 { set v [catch {execsql { INSERT INTO one VALUES(4,'four'); @@ -112,13 +112,13 @@ do_test trans-3.5 { SELECT a FROM two ORDER BY a; } altdb} msg] lappend v $msg -} {1 {database in use by another process}} +} {1 {database is locked}} do_test trans-3.6 { set v [catch {execsql { SELECT a FROM one ORDER BY a; } altdb} msg] lappend v $msg -} {1 {database in use by another process}} +} {1 {database is locked}} do_test trans-3.7 { set v [catch {execsql { INSERT INTO two VALUES(4,'IV'); @@ -130,13 +130,13 @@ do_test trans-3.8 { SELECT a FROM two ORDER BY a; } altdb} msg] lappend v $msg -} {1 {database in use by another process}} +} {1 {database is locked}} do_test trans-3.9 { set v [catch {execsql { SELECT a FROM one ORDER BY a; } altdb} msg] lappend v $msg -} {1 {database in use by another process}} +} {1 {database is locked}} do_test trans-3.10 { execsql {END TRANSACTION} } {} @@ -189,13 +189,13 @@ do_test trans-4.4 { SELECT a FROM two ORDER BY a; } altdb} msg] lappend v $msg -} {1 {database in use by another process}} +} {1 {database is locked}} do_test trans-4.5 { set v [catch {execsql { SELECT a FROM one ORDER BY a; } altdb} msg] lappend v $msg -} {1 {database in use by another process}} +} {1 {database is locked}} do_test trans-4.6 { set v [catch {execsql { BEGIN TRANSACTION; @@ -208,13 +208,13 @@ do_test trans-4.7 { SELECT a FROM two ORDER BY a; } altdb} msg] lappend v $msg -} {1 {database in use by another process}} +} {1 {database is locked}} do_test trans-4.8 { set v [catch {execsql { SELECT a FROM one ORDER BY a; } altdb} msg] lappend v $msg -} {1 {database in use by another process}} +} {1 {database is locked}} do_test trans-4.9 { set v [catch {execsql { END TRANSACTION; |