]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fixes to the locking and rollback behavior. (CVS 261)
authordrh <drh@noemail.net>
Sun, 23 Sep 2001 02:35:53 +0000 (02:35 +0000)
committerdrh <drh@noemail.net>
Sun, 23 Sep 2001 02:35:53 +0000 (02:35 +0000)
FossilOrigin-Name: 337b3d3b2a903328d9744c111979909a284b8348

18 files changed:
manifest
manifest.uuid
src/btree.c
src/btree.h
src/build.c
src/delete.c
src/insert.c
src/sqlite.h.in
src/test3.c
src/tokenize.c
src/update.c
src/util.c
src/vdbe.c
src/vdbe.h
test/btree.test
test/btree2.test
test/lock.test
test/trans.test

index 5d36a68a4ab396c751b117b4b7ebd4c9636f3f08..277ff7413013d429ec409c79ee67b7303611656e 100644 (file)
--- a/manifest
+++ b/manifest
@@ -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
index 3b85bd8519b08d49eb7e374ac794983fe415c922..f0500d753600bbd9250607caf3bdf491f2d5238c 100644 (file)
@@ -1 +1 @@
-9114420dd01d92cc8890046500a8806a297a4e65
\ No newline at end of file
+337b3d3b2a903328d9744c111979909a284b8348
\ No newline at end of file
index 09742e62c3b630efe7c4590d448a5f1d969155ab..909ead07d2189964418dc0a87e97319dd7657ea0 100644 (file)
@@ -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);
index 02cd9b59eda0caf2ea48f703af1b63ca2c10f6ab..4efd718e63c699198a3768c7ebb8546ffec9b360 100644 (file)
@@ -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,
index 2f05e368b32ffc132f1038aa6894424ba3df00b9..24862028cf64b60709700680e91440499e739478 100644 (file)
@@ -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;
 }
index 2b712307b7a67649cf1693b5b0ddcb42128bba5e..120295b5f3c5ed475cf8f1134042b377f3d33032 100644 (file)
@@ -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);
   }
 
index 3fcb61e70815c725cd716fa783a8d021d71f48d1..8de423f678f6634be30a48d383fab7dbb39a600b 100644 (file)
@@ -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);
   }
 
index 952983716d44e980c31cc7fcccdcf80f5ad52dfa..6d56498b5fb563a1bee7a0505f68f9d1b2ade3ee 100644 (file)
@@ -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
index 9dd084105d3090257cd81d5797e989de5476742b..978631159bed1f9c288ae9272e32400557cc2df8 100644 (file)
@@ -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;
index be97113215aeb1d9fb18cfc522c2fae73d5b5bef..53ebbbf1a2f8c34ef7db1c80de105a06c0ac49c4 100644 (file)
@@ -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;
     }
index 62c024118816b1189dfbb66a540bf340d1d8cf2a..fb67ac66bfc045da80b8b18ff61b2a6f0cc39872 100644 (file)
@@ -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);
   }
 
index 02f9443e2060efc3659f9ca0b74630d4c9b16250..e12c0a9e9e5a51fd045495cab03dd6bead960439 100644 (file)
@@ -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;
index 19fb9663c1cd08c3257d0f5e5e4046e9e9da7c75..88fcaa2a3c012fad616a23d01499f56ac2326473 100644 (file)
@@ -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>
@@ -675,6 +675,19 @@ static void cleanupCursor(Cursor *pCx){
   memset(pCx, 0, sizeof(Cursor));
 }
 
+/*
+** 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.
 **
@@ -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;
index b5634982a1a16f852fd5a7aa2b347bbee218785d..c1975bd172ebb61cbfd8fb5a7fcf51ced5f072cd 100644 (file)
@@ -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
index 8ba55a403758462d3bfebd2db7e1a6cd42e1aa70..97e7847f63f4de90e06093c65cfe4942bc74b3c9 100644 (file)
@@ -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
index 7b594caf8d821f56f31e974af4ac662d00b92f58..865d6b8bc6c25f7402a03f6e43d937b6d33206ff 100644 (file)
@@ -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
index c6eaf0bf8a5b1653979bfd25d58e51c9eb2bafab..b5d546a1e2d4bbea5456aae21d0727ce0bb00038 100644 (file)
 # 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\r
+# 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
index 1566b15403b3fc5e56e95d40897efc443bcfebae..8c2c6f81e2c0ee7f269c41ac65852069ba3f986d 100644 (file)
@@ -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;