]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the shared schema/pager modifications. Very few tests so far. (CVS 2859)
authordanielk1977 <danielk1977@noemail.net>
Thu, 5 Jan 2006 11:34:32 +0000 (11:34 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Thu, 5 Jan 2006 11:34:32 +0000 (11:34 +0000)
FossilOrigin-Name: deeda0dc06c1595aedd8d06a0c4e88a8abf78cf7

27 files changed:
main.mk
manifest
manifest.uuid
src/alter.c
src/analyze.c
src/attach.c
src/auth.c
src/btree.c
src/btree.h
src/build.c
src/delete.c
src/expr.c
src/insert.c
src/main.c
src/pragma.c
src/prepare.c
src/select.c
src/sqliteInt.h
src/trigger.c
src/update.c
src/vdbe.c
src/where.c
test/all.test
test/delete2.test
test/shared.test
test/tester.tcl
www/sharedcache.tcl [new file with mode: 0644]

diff --git a/main.mk b/main.mk
index ff6b843b25a719250d3d2cd8fd24283920f5229b..696114cefa3c441855341b71542ad6334468ab89 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -455,6 +455,9 @@ pragma.html:        $(TOP)/www/pragma.tcl
 lockingv3.html:        $(TOP)/www/lockingv3.tcl
        tclsh $(TOP)/www/lockingv3.tcl >lockingv3.html
 
+sharedcache.html: $(TOP)/www/sharedcache.tcl
+       tclsh $(TOP)/www/sharedcache.tcl >sharedcache.html
+
 mingw.html:    $(TOP)/www/mingw.tcl
        tclsh $(TOP)/www/mingw.tcl >mingw.html
 
@@ -535,6 +538,7 @@ DOC = \
   optoverview.html \
   pragma.html \
   quickstart.html \
+  sharedcache.html \
   speed.html \
   sqlite.html \
   support.html \
index b9e3b45a17dc7b36607c14257bccff6e5e680c91..256733b7a82da8c35a22be517a71045ca9694985 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Bug\sfix\sin\sthe\sIF\sNOT\sEXISTS\slogic.\s(CVS\s2858)
-D 2006-01-04T21:40:07
+C Add\sthe\sshared\sschema/pager\smodifications.\sVery\sfew\stests\sso\sfar.\s(CVS\s2859)
+D 2006-01-05T11:34:33
 F Makefile.in e3c6b3a38d734d41574c04f2fc90d18de2b87102
 F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -19,7 +19,7 @@ F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538
 F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
 F ltmain.sh f6b283068efa69f06eb8aa1fe4bddfdbdeb35826
-F main.mk c5c5087f957210233fd6ecb5e7c161c2c90038b9
+F main.mk a0d69de7dcb8f2d439412a78a58a999edc28561c
 F mkdll.sh 5ec23622515d5bf8969404e80cfb5e220ddf0512
 F mkopcodec.awk bd46ad001c98dfbab07b1713cb8e692fa0e5415d
 F mkopcodeh.awk 071dbba4eaf56c8d643baf4604a043af35683316
@@ -30,25 +30,25 @@ F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
 F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
 F sqlite3.def c413e514217736884254739a105c8c942fdf0c2f
 F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
-F src/alter.c 5905a3372379daa4f860199452b4de5a836e53f3
-F src/analyze.c ea42005eed52c382fcc7ef66969e7f1858597633
-F src/attach.c 07822dbd2dcf6de548aba6cb24142aec800fa3b6
-F src/auth.c 31e2304bef67f44d635655f44234387ea7d21454
-F src/btree.c ec89192ceeae9343c5d8f2564b8c9b8c958e6bfc
-F src/btree.h d6481f9253f0b5fa40b35da4b93a54d0f9c5f9f2
-F src/build.c 7bf6f9ba5b2ea7c654986085211fcd9794cfb5c8
+F src/alter.c e9deb3f4fd7c663a0d1f235d541bc5ea1f2cfa8b
+F src/analyze.c d821684cdb4d0403e327e4a3440a832e9e54fa3a
+F src/attach.c 999104c56a60b88eab11ef9c8f40dedf1650b287
+F src/auth.c cdec356a5cd8b217c346f816c5912221537fe87f
+F src/btree.c f848dd6e590f6bb93e2f229d87080c900d49bd4c
+F src/btree.h 96b8c00c6e11ff92f8d3d6a7a0ff358bd10d8f19
+F src/build.c 6b14101f1ed5328c815e12baec11dcec97eed096
 F src/callback.c 62066afd516f220575e81b1a1239ab92a2eae252
 F src/complete.c df1681cef40dec33a286006981845f87b194e7a4
 F src/date.c bb079317bff6a2b78aba5c0d2ddae5f6f03acfb7
-F src/delete.c ebb83753b6eca870c90edf2de736ab547685c326
+F src/delete.c 2479a8419c76cd4c13f2181d399425ec215ceeb9
 F src/experimental.c 50c1e3b34f752f4ac10c36f287db095c2b61766d
-F src/expr.c 714a06707facc5152228f2c1758d5003bd3a5b3c
+F src/expr.c ed2a272c7afd63232ca04881159ce2366266e35d
 F src/func.c 25f1e5710b71cb345b492a18088f546188599f6b
 F src/hash.c 8747cf51d12de46512880dfcf1b68b4e24072863
 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
-F src/insert.c a5629e462560d6aa9be7e0dafb6ed1518bb623bb
+F src/insert.c d167f9d41932ddaff9162f116e2abc514b0680b6
 F src/legacy.c 59757d857ab95fcbb0ac27692d3201e35f093dd7
-F src/main.c c93f80d1fcaf3ed508d62a163819b10a730c2fb7
+F src/main.c 3a9689e4127ad7d4d417ff4e0bed2dd2b76ea445
 F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217
 F src/os.c 7b4a002d9c9421580276db55d2329636a604e8ef
 F src/os.h cc99e1515696728ba64c77fffa781ebadea34619
@@ -62,14 +62,14 @@ F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
 F src/pager.c 681b4e39d581ead8fd54283176138bec924a4bae
 F src/pager.h e0acb095b3ad0bca48f2ab00c87346665643f64f
 F src/parse.y 58258759fabdd48f1d2561e276097290b1ea2680
-F src/pragma.c 8883b4d34796efa315bdd0ec1b03f580ef1575b9
-F src/prepare.c 7639314c504f602d87730238c44ccddc4407ac60
+F src/pragma.c 4af4041a88d41421b8ff2e5574d82d7b9d1e35b1
+F src/prepare.c 67ff283f7c71e32a91d8c843e758eb4bf68ab94e
 F src/printf.c f47a2f4b5387cd2ebb12e9117a1a5d6bd9a2b812
 F src/random.c ff5e9a8cad790e2a51cd4d2e7737dc8540e09d1d
-F src/select.c 5b0ccd6688e61c0720efa45075a3f9da60180554
+F src/select.c 7a78520fcd79daccd4e33721dbc977d2e5c5d95a
 F src/shell.c 66b073375efbdee19045e7e0cd38b85f9aff71da
 F src/sqlite.h.in ba3a29daa6a16e054191ccb384a981964e882a1d
-F src/sqliteInt.h a9b187e8621cd3c20c7ef6c0716d77cbed716d4d
+F src/sqliteInt.h 5117ce283868de7010e62ec1a80ce1162575c184
 F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316
 F src/tclsqlite.c 0386460a4c017d49d729d44cdcb3fb3ffb2503e0
 F src/test1.c 988dbac66c3ca92d69fbe0283d77e86cd6f73ce8
@@ -80,21 +80,21 @@ F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f
 F src/test6.c cb811391ec0b7c75f29e545d4820a9cf19f3637e
 F src/test_async.c a383aed2753e47d2ac4b5397c43f6ac216a237ae
 F src/tokenize.c 7a3a3d3cc734f684a77c4dfd09eb46fcee25394c
-F src/trigger.c 2925ba96d964d9b717e74006bf7e64b8a6b70d97
-F src/update.c ec8e540617b116725b5a55c8d6b4db8bc67fdd7d
+F src/trigger.c 858c0a4974035b002fd2192399c6076ac7b75e1f
+F src/update.c c72e9cbbc0adf8d728c1c39ace03d4adb29b5cfb
 F src/utf.c b7bffac4260177ae7f83c01d025fe0f5ed70ce71
 F src/util.c a690bbf549fc5c465384f624e90c009935b6d18b
 F src/vacuum.c fbfdd3967fd34e2f260fafed88dcbf3c10856b94
-F src/vdbe.c 99bfdfda01747e11f74cb891312a669023ab355a
+F src/vdbe.c ad844cc360807ad00e513d17d5a7cf8d844cb86c
 F src/vdbe.h 8729a4ee16ff9aeab2af9667df3cf300ff978e13
 F src/vdbeInt.h 9b78ba00cc006bff17e04a54ba3ded9fc7810a10
 F src/vdbeapi.c b270b680cbc5d20b5a1abfdb08339667985df94e
 F src/vdbeaux.c 7d55232a7e8c21899bbfc9ba3bd2469eb32faaf0
 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
 F src/vdbemem.c deba8d6e3727643924b210a8c531a496c2b8d386
-F src/where.c 0296a20c2e2a39c0cb785efe471fd1958a5259f3
+F src/where.c 3ec45076e7cce523aad34eaf9bd119237b56942a
 F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42
-F test/all.test 55706917a12cd616440d50c35323747b4a9f03c3
+F test/all.test 18602f8cd97d5741574dee7288e1f5f427f12825
 F test/alter.test b94b640063e725d062b2997bd2810ac39195c718
 F test/alter2.test cc0b8832e4e98605dbc26910efd4bb89abe59cb2
 F test/alter3.test 6e144ea3dcc395afcc28e794bb532be83dc8fdcb
@@ -145,7 +145,7 @@ F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
 F test/date.test 30ca15e608a45d868fd419c901795382efe27020
 F test/default.test 252298e42a680146b1dd64f563b95bdf088d94fb
 F test/delete.test 525a6953bc3978780cae35f3eaf1027cf4ce887d
-F test/delete2.test d97a85b487f0ad2b391026ffade125549351f5cf
+F test/delete2.test d20b08733243f1890079f3b48f2356fbb62212b2
 F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
 F test/descidx1.test 466a9b87f3a7682156916dcbd5b77be55fd39d75
 F test/descidx2.test f9f73c562932b81207faa525cd52acdfd2fc0482
@@ -221,7 +221,7 @@ F test/select4.test c239f516aa31f42f2ef7c6d7cd01105f08f934ca
 F test/select5.test 07a90ab3c7e3f0a241a9cdea1d997b2c8a89ff0b
 F test/select6.test f459a19bdac0501c4d3eb1a4df4b7a76f1bb8ad4
 F test/select7.test 1bf795b948c133a15a2a5e99d3270e652ec58ce6
-F test/shared.test 1f68f8aecf5064d1da2fb2f316bbb6e70054f08e
+F test/shared.test aa054381c8fe21d7f46dc1d460ac85f675297b26
 F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5
 F test/subquery.test e6de53332c0301b3cfa34edc3f3cd5fa1e859efd
 F test/subselect.test 2d13fb7f450db3595adcdd24079a0dd1d2d6abc2
@@ -230,7 +230,7 @@ F test/table.test 6dc0dfa44dd429520e0e5a0c5e55025f730e9403
 F test/tableapi.test 6a66d58b37d46dc0f2b3c7d4bd2617d209399bd1
 F test/tclsqlite.test 19578d32a7692311918caf0ae3521d19525bcb62
 F test/temptable.test 7927261befdbc7b0a7ffebb85ecc70a74fa7b15b
-F test/tester.tcl 9ab7dc33002fae680bf5f9a117a2747b01662069
+F test/tester.tcl 58dcfde5265c3c46e05ae2af4accaa29f0b44d91
 F test/thread1.test 776c9e459b75ba905193b351926ac4019b049f35
 F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
 F test/threadtest2.c 97a830d53c24c42290501fdfba4a6e5bdd34748b
@@ -327,6 +327,7 @@ F www/optimizing.tcl f0b2538988d1bbad16cbfe63ec6e8f48c9eb04e5
 F www/optoverview.tcl 815df406a38c9f69b27d37e8f7ede004c6d9f19e
 F www/pragma.tcl 44f7b665ca598ad24724f35991653638a36a6e3f
 F www/quickstart.tcl 2f3daf8038e82a102e1e8cc877aafa7a413f5f11
+F www/sharedcache.tcl c42098d1436bcb54ec7f08d07c2e75316e2dde68
 F www/speed.tcl 656ed5be8cc9d536353e1a96927b925634a62933
 F www/sqlite.tcl a883ed7b47371d31d471e6aea5ed1f972ae8e1be
 F www/support.tcl 7961ce16290692578d783bb11f0dc8391a9be9c3
@@ -335,7 +336,7 @@ F www/tclsqlite.tcl ddcf912ea48695603c8ed7efb29f0812ef8d1b49
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P d0e3d466094f7b2f74ed7ebb324e5024ea8faa6f
-R a1f52f5cef7ff73c69c22ea9cb8c2ecf
-U drh
-Z 0221548bd916e287249dc78e673eb685
+P cb9095ac52e76926f274678ef55ebb9df4b9fcac
+R a8dde2ab271da0ac5619066fd6022d05
+U danielk1977
+Z 0d100378c01552c949846ec200b31003
index f70b75948be0839b5ed0b764ef93714a8121c1f8..a50e26f5548e5eabff5c9269b49d3202b4c611be 100644 (file)
@@ -1 +1 @@
-cb9095ac52e76926f274678ef55ebb9df4b9fcac
\ No newline at end of file
+deeda0dc06c1595aedd8d06a0c4e88a8abf78cf7
\ No newline at end of file
index 77109c78352a4c08f130d6f0da8fe01ca942e001..d99001161897df3b16002a3454ab6062aac3c15f 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that used to generate VDBE code
 ** that implements the ALTER TABLE command.
 **
-** $Id: alter.c,v 1.13 2005/12/21 14:43:12 drh Exp $
+** $Id: alter.c,v 1.14 2006/01/05 11:34:33 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -177,9 +177,16 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
   Trigger *pTrig;
   char *zWhere = 0;
   char *tmp = 0;
-  if( pTab->iDb!=1 ){
+  const DbSchema *pTempSchema = pParse->db->aDb[1].pSchema; /* Temp db schema */
+
+  /* If the table is not located in the temp-db (in which case NULL is 
+  ** returned, loop through the tables list of triggers. For each trigger
+  ** that is not part of the temp-db schema, add a clause to the WHERE 
+  ** expression being built up in zWhere.
+  */
+  if( pTab->pSchema!=pTempSchema ){
     for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){
-      if( pTrig->iDb==1 ){
+      if( pTrig->pSchema==pTempSchema ){
         if( !zWhere ){
           zWhere = sqlite3MPrintf("name=%Q", pTrig->name);
         }else{
@@ -204,20 +211,22 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
 static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
   Vdbe *v;
   char *zWhere;
-  int iDb;
+  int iDb;                   /* Index of database containing pTab */
 #ifndef SQLITE_OMIT_TRIGGER
   Trigger *pTrig;
 #endif
 
   v = sqlite3GetVdbe(pParse);
   if( !v ) return;
-  iDb = pTab->iDb;
+  iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+  assert( iDb>=0 );
 
 #ifndef SQLITE_OMIT_TRIGGER
   /* Drop any table triggers from the internal schema. */
   for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){
-    assert( pTrig->iDb==iDb || pTrig->iDb==1 );
-    sqlite3VdbeOp3(v, OP_DropTrigger, pTrig->iDb, 0, pTrig->name, 0);
+    int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
+    assert( iTrigDb==iDb || iTrigDb==1 );
+    sqlite3VdbeOp3(v, OP_DropTrigger, iTrigDb, 0, pTrig->name, 0);
   }
 #endif
 
@@ -263,7 +272,7 @@ void sqlite3AlterRenameTable(
 
   pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase);
   if( !pTab ) goto exit_rename_table;
-  iDb = pTab->iDb;
+  iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
   zDb = db->aDb[iDb].zName;
 
   /* Get a NULL terminated version of the new table name. */
@@ -390,7 +399,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
   pNew = pParse->pNewTable;
   assert( pNew );
 
-  iDb = pNew->iDb;
+  iDb = sqlite3SchemaToIndex(pParse->db, pNew->pSchema);
   zDb = pParse->db->aDb[iDb].zName;
   zTab = pNew->zName;
   pCol = &pNew->aCol[pNew->nCol-1];
@@ -490,7 +499,6 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
   int i;
   int nAlloc;
 
-
   /* Look up the table being altered. */
   assert( pParse->pNewTable==0 );
   if( sqlite3Tsd()->mallocFailed ) goto exit_begin_add_column;
@@ -504,7 +512,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
   }
 
   assert( pTab->addColOffset>0 );
-  iDb = pTab->iDb;
+  iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
 
   /* Put a copy of the Table struct in Parse.pNewTable for the
   ** sqlite3AddColumn() function and friends to modify.
@@ -529,7 +537,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
     pCol->zType = 0;
     pCol->pDflt = 0;
   }
-  pNew->iDb = iDb;
+  pNew->pSchema = pParse->db->aDb[iDb].pSchema;
   pNew->addColOffset = pTab->addColOffset;
   pNew->nRef = 1;
 
index 41fecb13ba63b6ff6a696266fde869a498936f24..3bcc8add4110c483913e2452e598af232bbcd879 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code associated with the ANALYZE command.
 **
-** @(#) $Id: analyze.c,v 1.11 2005/11/14 22:29:05 drh Exp $
+** @(#) $Id: analyze.c,v 1.12 2006/01/05 11:34:33 danielk1977 Exp $
 */
 #ifndef SQLITE_OMIT_ANALYZE
 #include "sqliteInt.h"
@@ -86,6 +86,7 @@ static void analyzeOneTable(
   int topOfLoop;   /* The top of the loop */
   int endOfLoop;   /* The end of the loop */
   int addr;        /* The address of an instruction */
+  int iDb;         /* Index of database containing pTab */
 
   v = sqlite3GetVdbe(pParse);
   if( pTab==0 || pTab->pIndex==0 ){
@@ -93,9 +94,11 @@ static void analyzeOneTable(
     return;
   }
 
+  iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+  assert( iDb>=0 );
 #ifndef SQLITE_OMIT_AUTHORIZATION
   if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0,
-      pParse->db->aDb[pTab->iDb].zName ) ){
+      pParse->db->aDb[iDb].zName ) ){
     return;
   }
 #endif
@@ -104,7 +107,8 @@ static void analyzeOneTable(
   for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
     /* Open a cursor to the index to be analyzed
     */
-    sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+    assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) );
+    sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
     VdbeComment((v, "# %s", pIdx->zName));
     sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum,
                      (char*)&pIdx->keyInfo, P3_KEYINFO);
@@ -215,6 +219,7 @@ static void loadAnalysis(Parse *pParse, int iDb){
 */
 static void analyzeDatabase(Parse *pParse, int iDb){
   sqlite3 *db = pParse->db;
+  DbSchema *pSchema = db->aDb[iDb].pSchema;    /* Schema of database iDb */
   HashElem *k;
   int iStatCur;
   int iMem;
@@ -223,7 +228,7 @@ static void analyzeDatabase(Parse *pParse, int iDb){
   iStatCur = pParse->nTab++;
   openStatTable(pParse, iDb, iStatCur, 0);
   iMem = pParse->nMem;
-  for(k=sqliteHashFirst(&db->aDb[iDb].tblHash);  k; k=sqliteHashNext(k)){
+  for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
     Table *pTab = (Table*)sqliteHashData(k);
     analyzeOneTable(pParse, pTab, iStatCur, iMem);
   }
@@ -239,7 +244,7 @@ static void analyzeTable(Parse *pParse, Table *pTab){
   int iStatCur;
 
   assert( pTab!=0 );
-  iDb = pTab->iDb;
+  iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
   sqlite3BeginWriteOperation(pParse, 0, iDb);
   iStatCur = pParse->nTab++;
   openStatTable(pParse, iDb, iStatCur, pTab->zName);
@@ -361,7 +366,7 @@ void sqlite3AnalysisLoad(sqlite3 *db, int iDb){
   char *zSql;
 
   /* Clear any prior statistics */
-  for(i=sqliteHashFirst(&db->aDb[iDb].idxHash); i; i=sqliteHashNext(i)){
+  for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
     Index *pIdx = sqliteHashData(i);
     sqlite3DefaultRowEst(pIdx);
   }
index 35af84521983c629eb6ec24bef9c81b13c7dcb11..ef6f281d726605c681131de99a5fc556d749766d 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to implement the ATTACH and DETACH commands.
 **
-** $Id: attach.c,v 1.38 2005/12/29 23:04:02 drh Exp $
+** $Id: attach.c,v 1.39 2006/01/05 11:34:33 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -116,15 +116,20 @@ static void attachFunc(
   db->aDb = aNew;
   aNew = &db->aDb[db->nDb++];
   memset(aNew, 0, sizeof(*aNew));
-  sqlite3HashInit(&aNew->tblHash, SQLITE_HASH_STRING, 0);
-  sqlite3HashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0);
-  sqlite3HashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0);
-  sqlite3HashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1);
+
+  /* Open the database file. If the btree is successfully opened, use
+  ** it to obtain the database schema. At this point the schema may
+  ** or may not be initialised.
+  */
+  rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt);
+  if( rc==SQLITE_OK ){
+    aNew->pSchema = sqlite3SchemaGet(aNew->pBt);
+    if( !aNew->pSchema ){
+      rc = SQLITE_NOMEM;
+    }
+  }
   aNew->zName = sqliteStrDup(zName);
   aNew->safety_level = 3;
-  
-  /* Open the database file */
-  rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt);
 
 #if SQLITE_HAS_CODEC
   {
index 7627ee140aa7dac59642dee788a400376c5cfc6c..b24976e573e4c84814e039d42f8dbd9f25b28a55 100644 (file)
@@ -14,7 +14,7 @@
 ** systems that do not need this facility may omit it by recompiling
 ** the library with -DSQLITE_OMIT_AUTHORIZATION=1
 **
-** $Id: auth.c,v 1.22 2005/07/29 15:36:15 drh Exp $
+** $Id: auth.c,v 1.23 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -112,10 +112,12 @@ void sqlite3AuthRead(
   int iSrc;             /* Index in pTabList->a[] of table being read */
   const char *zDBase;   /* Name of database being accessed */
   TriggerStack *pStack; /* The stack of current triggers */
+  int iDb;              /* The index of the database the expression refers to */
 
   if( db->xAuth==0 ) return;
   if( pExpr->op==TK_AS ) return;
   assert( pExpr->op==TK_COLUMN );
+  iDb = sqlite3SchemaToIndex(pParse->db, pExpr->pSchema);
   for(iSrc=0; pTabList && iSrc<pTabList->nSrc; iSrc++){
     if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break;
   }
@@ -140,14 +142,14 @@ void sqlite3AuthRead(
   }else{
     zCol = "ROWID";
   }
-  assert( pExpr->iDb<db->nDb );
-  zDBase = db->aDb[pExpr->iDb].zName;
+  assert( iDb<db->nDb );
+  zDBase = db->aDb[iDb].zName;
   rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase, 
                  pParse->zAuthContext);
   if( rc==SQLITE_IGNORE ){
     pExpr->op = TK_NULL;
   }else if( rc==SQLITE_DENY ){
-    if( db->nDb>2 || pExpr->iDb!=0 ){
+    if( db->nDb>2 || iDb!=0 ){
       sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited", 
          zDBase, pTab->zName, zCol);
     }else{
index 7e5314b581e193d1fe41cd65174a649e57489cf0..636bee7f74882424810157c1843780e4c137c3d1 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.278 2005/12/30 16:31:54 danielk1977 Exp $
+** $Id: btree.c,v 1.279 2006/01/05 11:34:34 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -349,6 +349,8 @@ struct BtShared {
   int nRef;             /* Number of references to this structure */
   int nTransaction;     /* Number of open transactions (read + write) */
   BtLock *pLock;        /* List of locks held on this shared-btree struct */
+  void *pSchema;
+  void (*xFreeSchema)(void*);
 };
 
 /*
@@ -382,9 +384,38 @@ struct BtCursor {
   int idx;                  /* Index of the entry in pPage->aCell[] */
   CellInfo info;            /* A parse of the cell we are pointing at */
   u8 wrFlag;                /* True if writable */
-  u8 isValid;               /* TRUE if points to a valid entry */
+  u8 eState;                /* One of the CURSOR_XXX constants (see below) */
+#ifndef SQLITE_OMIT_SHARED_CACHE
+  void *pKey;
+  i64 nKey;
+  int skip;        /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */
+#endif
 };
 
+/*
+** Potential values for BtCursor.eState. The first two values (VALID and 
+** INVALID) may occur in any build. The third (REQUIRESEEK) may only occur 
+** if sqlite was compiled without the OMIT_SHARED_CACHE symbol defined.
+**
+** CURSOR_VALID:
+**   Cursor points to a valid entry. getPayload() etc. may be called.
+**
+** CURSOR_INVALID:
+**   Cursor does not point to a valid entry. This can happen (for example) 
+**   because the table is empty or because BtreeCursorFirst() has not been
+**   called.
+**
+** CURSOR_REQUIRESEEK:
+**   The table that this cursor was opened on still exists, but has been 
+**   modified since the cursor was last used. The cursor position is saved
+**   in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in 
+**   this state, restoreCursorPosition() can be called to attempt to seek 
+**   the cursor to the saved position.
+*/
+#define CURSOR_INVALID           0
+#define CURSOR_VALID             1
+#define CURSOR_REQUIRESEEK       2
+
 /*
 ** The TRACE macro will print high-level status information about the
 ** btree operation when the global variable sqlite3_btree_trace is
@@ -462,14 +493,110 @@ struct BtLock {
   ** manipulate entries in the BtShared.pLock linked list used to store
   ** shared-cache table level locks. If the library is compiled with the
   ** shared-cache feature disabled, then there is only ever one user
-  ** of each BtShared structure and so this locking is not required
-  ** So define the three interface functions as no-ops.
+  ** of each BtShared structure and so this locking is not necessary
+  ** So define the lock related functions as no-ops.
   */
   #define queryTableLock(a,b,c) SQLITE_OK
   #define lockTable(a,b,c) SQLITE_OK
-  #define unlockAllTables(a,b,c)
+  #define unlockAllTables(a)
+  #define restoreCursorPosition(a,b) SQLITE_OK
+  #define saveAllCursors(a,b,c) SQLITE_OK
+
 #else
 
+/*
+** Save the current cursor position in the variables BtCursor.nKey 
+** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
+*/
+static int saveCursorPosition(BtCursor *pCur){
+  int rc = SQLITE_OK;
+
+  assert( CURSOR_VALID==pCur->eState|| CURSOR_INVALID==pCur->eState );
+  assert( 0==pCur->pKey );
+
+  if( pCur->eState==CURSOR_VALID ){
+    rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
+
+    /* If this is an intKey table, then the above call to BtreeKeySize()
+    ** stores the integer key in pCur->nKey. In this case this value is
+    ** all that is required. Otherwise, if pCur is not open on an intKey
+    ** table, then malloc space for and store the pCur->nKey bytes of key 
+    ** data.
+    */
+    if( rc==SQLITE_OK && 0==pCur->pPage->intKey){
+      void *pKey = sqliteMalloc(pCur->nKey);
+      if( pKey ){
+        rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
+        if( pKey ){
+          pCur->pKey = pKey;
+        }else{
+          sqliteFree(pKey);
+        }
+      }else{
+        rc = SQLITE_NOMEM;
+      }
+    }
+    assert( !pCur->pPage->intKey || !pCur->pKey );
+
+    /* Todo: Should we drop the reference to pCur->pPage here? */
+
+    if( rc==SQLITE_OK ){
+      pCur->eState = CURSOR_REQUIRESEEK;
+    }
+  }
+
+  return rc;
+}
+
+/*
+** Save the positions of all cursors except pExcept open on the table 
+** with root-page iRoot. Usually, this is called just before cursor
+** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
+*/
+static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
+  BtCursor *p;
+  if( sqlite3Tsd()->useSharedData ){
+    for(p=pBt->pCursor; p; p=p->pNext){
+      if( p!=pExcept && p->pgnoRoot==iRoot && p->eState==CURSOR_VALID ){
+        int rc = saveCursorPosition(p);
+        if( SQLITE_OK!=rc ){
+          return rc;
+        }
+      }
+    }
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Restore the cursor to the position it was in (or as close to as possible)
+** when saveCursorPosition() was called. Note that this call deletes the 
+** saved position info stored by saveCursorPosition(), so there can be
+** at most one effective restoreCursorPosition() call after each 
+** saveCursorPosition().
+**
+** If the second argument argument - doSeek - is false, then instead of 
+** returning the cursor to it's saved position, any saved position is deleted
+** and the cursor state set to CURSOR_INVALID.
+*/
+static int restoreCursorPosition(BtCursor *pCur, int doSeek){
+  int rc = SQLITE_OK;
+  if( pCur->eState==CURSOR_REQUIRESEEK ){
+    assert( sqlite3Tsd()->useSharedData );
+    if( doSeek ){
+      rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip);
+    }else{
+      pCur->eState = CURSOR_INVALID;
+    }
+    if( rc==SQLITE_OK ){
+      sqliteFree(pCur->pKey);
+      pCur->pKey = 0;
+      assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState );
+    }
+  }
+  return rc;
+}
+
 /*
 ** Query to see if btree handle p may obtain a lock of type eLock 
 ** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return
@@ -480,10 +607,36 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){
   BtShared *pBt = p->pBt;
   BtLock *pIter;
 
-  for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
-    if( pIter->pBtree!=p && pIter->iTable==iTab && 
-        (pIter->eLock!=READ_LOCK || eLock!=READ_LOCK) ){
-      return SQLITE_BUSY;
+  /* This is a no-op if the shared-cache is not enabled */
+  if( 0==sqlite3Tsd()->useSharedData ){
+    return SQLITE_OK;
+  }
+
+  /* This (along with lockTable()) is where the ReadUncommitted flag is
+  ** dealt with. If the caller is querying for a read-lock and the flag is
+  ** set, it is unconditionally granted - even if there are write-locks
+  ** on the table. If a write-lock is requested, the ReadUncommitted flag
+  ** is not considered.
+  **
+  ** In function lockTable(), if a read-lock is demanded and the 
+  ** ReadUncommitted flag is set, no entry is added to the locks list 
+  ** (BtShared.pLock).
+  **
+  ** To summarize: If the ReadUncommitted flag is set, then read cursors do
+  ** not create or respect table locks. The locking procedure for a 
+  ** write-cursor does not change.
+  */
+  if( 
+    !p->pSqlite || 
+    0==(p->pSqlite->flags&SQLITE_ReadUncommitted) || 
+    eLock==WRITE_LOCK ||
+    iTab==1
+  ){
+    for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
+      if( pIter->pBtree!=p && pIter->iTable==iTab && 
+          (pIter->eLock!=eLock || eLock!=READ_LOCK) ){
+        return SQLITE_BUSY;
+      }
     }
   }
   return SQLITE_OK;
@@ -502,8 +655,27 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
   BtLock *pLock = 0;
   BtLock *pIter;
 
+  /* This is a no-op if the shared-cache is not enabled */
+  if( 0==sqlite3Tsd()->useSharedData ){
+    return SQLITE_OK;
+  }
+
   assert( SQLITE_OK==queryTableLock(p, iTable, eLock) );
 
+  /* If the read-uncommitted flag is set and a read-lock is requested,
+  ** return early without adding an entry to the BtShared.pLock list. See
+  ** comment in function queryTableLock() for more info on handling 
+  ** the ReadUncommitted flag.
+  */
+  if( 
+    (p->pSqlite) && 
+    (p->pSqlite->flags&SQLITE_ReadUncommitted) && 
+    (eLock==READ_LOCK) &&
+    iTable!=1
+  ){
+    return SQLITE_OK;
+  }
+
   /* First search the list for an existing lock on this table. */
   for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
     if( pIter->iTable==iTable && pIter->pBtree==p ){
@@ -544,6 +716,13 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
 */
 static void unlockAllTables(Btree *p){
   BtLock **ppIter = &p->pBt->pLock;
+
+  /* If the shared-cache extension is not enabled, there should be no
+  ** locks in the BtShared.pLock list, making this procedure a no-op. Assert
+  ** that this is the case.
+  */
+  assert( sqlite3Tsd()->useSharedData || 0==*ppIter );
+
   while( *ppIter ){
     BtLock *pLock = *ppIter;
     if( pLock->pBtree==p ){
@@ -680,27 +859,6 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
 
 #endif /* SQLITE_OMIT_AUTOVACUUM */
 
-/*
-** Return a pointer to the Btree structure associated with btree pBt
-** and connection handle pSqlite.
-*/
-#if 0
-static Btree *btree_findref(BtShared *pBt, sqlite3 *pSqlite){
-#ifndef SQLITE_OMIT_SHARED_DATA
-  if( pBt->aRef ){
-    int i;
-    for(i=0; i<pBt->nRef; i++){
-      if( pBt->aRef[i].pSqlite==pSqlite ){
-        return &pBt->aRef[i];
-      }
-    }
-    assert(0);
-  }
-#endif
-  return &pBt->ref;
-}
-#endif
-
 /*
 ** Given a btree page and a cell index (0 means the first cell on
 ** the page, 1 means the second cell, and so forth) return a pointer
@@ -1386,7 +1544,9 @@ int sqlite3BtreeOpen(
   int rc;
   int nReserve;
   unsigned char zDbHeader[100];
+#ifndef SQLITE_OMIT_SHARED_CACHE
   SqliteTsd *pTsd = sqlite3Tsd();
+#endif
 
   /* Set the variable isMemdb to true for an in-memory database, or 
   ** false for a file-based database. This symbol is only required if
@@ -1501,8 +1661,8 @@ int sqlite3BtreeOpen(
     pBt->pNext = pTsd->pBtree;
     pTsd->pBtree = pBt;
   }
-  pBt->nRef = 1;
 #endif
+  pBt->nRef = 1;
   *ppBtree = p;
   return SQLITE_OK;
 }
@@ -1511,10 +1671,13 @@ int sqlite3BtreeOpen(
 ** Close an open database and invalidate all cursors.
 */
 int sqlite3BtreeClose(Btree *p){
-  SqliteTsd *pTsd = sqlite3Tsd();
   BtShared *pBt = p->pBt;
   BtCursor *pCur;
 
+#ifndef SQLITE_OMIT_SHARED_CACHE
+  SqliteTsd *pTsd = sqlite3Tsd();
+#endif
+
   /* Drop any table-locks */
   unlockAllTables(p);
 
@@ -1556,6 +1719,9 @@ int sqlite3BtreeClose(Btree *p){
   /* Close the pager and free the shared-btree structure */
   assert( !pBt->pCursor );
   sqlite3pager_close(pBt->pPager);
+  if( pBt->xFreeSchema && pBt->pSchema ){
+    pBt->xFreeSchema(pBt->pSchema);
+  }
   sqliteFree(pBt);
   return SQLITE_OK;
 }
@@ -2323,7 +2489,7 @@ void sqlite3BtreeCursorList(Btree *p){
     sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
        pCur, pCur->pgnoRoot, zMode,
        pPage ? pPage->pgno : 0, pCur->idx,
-       pCur->isValid ? "" : " eof"
+       (pCur->eState==CURSOR_VALID) ? "" : " eof"
     );
   }
 }
@@ -2532,7 +2698,7 @@ int sqlite3BtreeCursor(
       return rc;
     }
   }
-  pCur = sqliteMallocRaw( sizeof(*pCur) );
+  pCur = sqliteMalloc( sizeof(*pCur) );
   if( pCur==0 ){
     rc = SQLITE_NOMEM;
     goto create_cursor_exception;
@@ -2571,7 +2737,7 @@ int sqlite3BtreeCursor(
   }
   pCur->pPrev = 0;
   pBt->pCursor = pCur;
-  pCur->isValid = 0;
+  pCur->eState = CURSOR_INVALID;
   *ppCur = pCur;
 
   return SQLITE_OK;
@@ -2604,6 +2770,7 @@ void sqlite3BtreeSetCompare(
 */
 int sqlite3BtreeCloseCursor(BtCursor *pCur){
   BtShared *pBt = pCur->pBtree->pBt;
+  restoreCursorPosition(pCur, 0);
   if( pCur->pPrev ){
     pCur->pPrev->pNext = pCur->pNext;
   }else{
@@ -2670,13 +2837,17 @@ static void getCellInfo(BtCursor *pCur){
 ** itself, not the number of bytes in the key.
 */
 int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
-  if( !pCur->isValid ){
-    *pSize = 0;
-  }else{
-    getCellInfo(pCur);
-    *pSize = pCur->info.nKey;
+  int rc = restoreCursorPosition(pCur, 1);
+  if( rc==SQLITE_OK ){
+    assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
+    if( pCur->eState==CURSOR_INVALID ){
+      *pSize = 0;
+    }else{
+      getCellInfo(pCur);
+      *pSize = pCur->info.nKey;
+    }
   }
-  return SQLITE_OK;
+  return rc;
 }
 
 /*
@@ -2687,14 +2858,18 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
 ** the database is empty) then *pSize is set to 0.
 */
 int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
-  if( !pCur->isValid ){
-    /* Not pointing at a valid entry - set *pSize to 0. */
-    *pSize = 0;
-  }else{
-    getCellInfo(pCur);
-    *pSize = pCur->info.nData;
+  int rc = restoreCursorPosition(pCur, 1);
+  if( rc==SQLITE_OK ){
+    assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
+    if( pCur->eState==CURSOR_INVALID ){
+      /* Not pointing at a valid entry - set *pSize to 0. */
+      *pSize = 0;
+    }else{
+      getCellInfo(pCur);
+      *pSize = pCur->info.nData;
+    }
   }
-  return SQLITE_OK;
+  return rc;
 }
 
 /*
@@ -2722,7 +2897,7 @@ static int getPayload(
   u32 nKey;
 
   assert( pCur!=0 && pCur->pPage!=0 );
-  assert( pCur->isValid );
+  assert( pCur->eState==CURSOR_VALID );
   pBt = pCur->pBtree->pBt;
   pPage = pCur->pPage;
   pageIntegrity(pPage);
@@ -2798,14 +2973,18 @@ static int getPayload(
 ** the available payload.
 */
 int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
-  assert( pCur->isValid );
-  assert( pCur->pPage!=0 );
-  if( pCur->pPage->intKey ){
-    return SQLITE_CORRUPT_BKPT;
+  int rc = restoreCursorPosition(pCur, 1);
+  if( rc==SQLITE_OK ){
+    assert( pCur->eState==CURSOR_VALID );
+    assert( pCur->pPage!=0 );
+    if( pCur->pPage->intKey ){
+      return SQLITE_CORRUPT_BKPT;
+    }
+    assert( pCur->pPage->intKey==0 );
+    assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
+    rc = getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
   }
-  assert( pCur->pPage->intKey==0 );
-  assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
-  return getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
+  return rc;
 }
 
 /*
@@ -2818,10 +2997,14 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
 ** the available payload.
 */
 int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
-  assert( pCur->isValid );
-  assert( pCur->pPage!=0 );
-  assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
-  return getPayload(pCur, offset, amt, pBuf, 1);
+  int rc = restoreCursorPosition(pCur, 1);
+  if( rc==SQLITE_OK ){
+    assert( pCur->eState==CURSOR_VALID );
+    assert( pCur->pPage!=0 );
+    assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
+    rc = getPayload(pCur, offset, amt, pBuf, 1);
+  }
+  return rc;
 }
 
 /*
@@ -2854,7 +3037,7 @@ static const unsigned char *fetchPayload(
   int nLocal;
 
   assert( pCur!=0 && pCur->pPage!=0 );
-  assert( pCur->isValid );
+  assert( pCur->eState==CURSOR_VALID );
   pPage = pCur->pPage;
   pageIntegrity(pPage);
   assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
@@ -2892,10 +3075,16 @@ static const unsigned char *fetchPayload(
 ** in the common case where no overflow pages are used.
 */
 const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){
-  return (const void*)fetchPayload(pCur, pAmt, 0);
+  if( pCur->eState==CURSOR_VALID ){
+    return (const void*)fetchPayload(pCur, pAmt, 0);
+  }
+  return 0;
 }
 const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
-  return (const void*)fetchPayload(pCur, pAmt, 1);
+  if( pCur->eState==CURSOR_VALID ){
+    return (const void*)fetchPayload(pCur, pAmt, 1);
+  }
+  return 0;
 }
 
 
@@ -2909,7 +3098,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
   MemPage *pOldPage;
   BtShared *pBt = pCur->pBtree->pBt;
 
-  assert( pCur->isValid );
+  assert( pCur->eState==CURSOR_VALID );
   rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
   if( rc ) return rc;
   pageIntegrity(pNewPage);
@@ -2956,7 +3145,7 @@ static void moveToParent(BtCursor *pCur){
   MemPage *pPage;
   int idxParent;
 
-  assert( pCur->isValid );
+  assert( pCur->eState==CURSOR_VALID );
   pPage = pCur->pPage;
   assert( pPage!=0 );
   assert( !isRootPage(pPage) );
@@ -2981,9 +3170,10 @@ static int moveToRoot(BtCursor *pCur){
   int rc;
   BtShared *pBt = pCur->pBtree->pBt;
 
+  restoreCursorPosition(pCur, 0);
   rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0);
   if( rc ){
-    pCur->isValid = 0;
+    pCur->eState = CURSOR_INVALID;
     return rc;
   }
   releasePage(pCur->pPage);
@@ -2996,10 +3186,10 @@ static int moveToRoot(BtCursor *pCur){
     assert( pRoot->pgno==1 );
     subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]);
     assert( subpage>0 );
-    pCur->isValid = 1;
+    pCur->eState = CURSOR_VALID;
     rc = moveToChild(pCur, subpage);
   }
-  pCur->isValid = pCur->pPage->nCell>0;
+  pCur->eState = ((pCur->pPage->nCell>0)?CURSOR_VALID:CURSOR_INVALID);
   return rc;
 }
 
@@ -3012,7 +3202,7 @@ static int moveToLeftmost(BtCursor *pCur){
   int rc;
   MemPage *pPage;
 
-  assert( pCur->isValid );
+  assert( pCur->eState==CURSOR_VALID );
   while( !(pPage = pCur->pPage)->leaf ){
     assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
     pgno = get4byte(findCell(pPage, pCur->idx));
@@ -3034,7 +3224,7 @@ static int moveToRightmost(BtCursor *pCur){
   int rc;
   MemPage *pPage;
 
-  assert( pCur->isValid );
+  assert( pCur->eState==CURSOR_VALID );
   while( !(pPage = pCur->pPage)->leaf ){
     pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
     pCur->idx = pPage->nCell;
@@ -3054,7 +3244,7 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
   int rc;
   rc = moveToRoot(pCur);
   if( rc ) return rc;
-  if( pCur->isValid==0 ){
+  if( pCur->eState==CURSOR_INVALID ){
     assert( pCur->pPage->nCell==0 );
     *pRes = 1;
     return SQLITE_OK;
@@ -3073,12 +3263,12 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
   int rc;
   rc = moveToRoot(pCur);
   if( rc ) return rc;
-  if( pCur->isValid==0 ){
+  if( CURSOR_INVALID==pCur->eState ){
     assert( pCur->pPage->nCell==0 );
     *pRes = 1;
     return SQLITE_OK;
   }
-  assert( pCur->isValid );
+  assert( pCur->eState==CURSOR_VALID );
   *pRes = 0;
   rc = moveToRightmost(pCur);
   return rc;
@@ -3117,7 +3307,7 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
   if( rc ) return rc;
   assert( pCur->pPage );
   assert( pCur->pPage->isInit );
-  if( pCur->isValid==0 ){
+  if( pCur->eState==CURSOR_INVALID ){
     *pRes = -1;
     assert( pCur->pPage->nCell==0 );
     return SQLITE_OK;
@@ -3209,7 +3399,11 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
 ** the first entry.  TRUE is also returned if the table is empty.
 */
 int sqlite3BtreeEof(BtCursor *pCur){
-  return pCur->isValid==0;
+  /* TODO: What if the cursor is in CURSOR_REQUIRESEEK but all table entries
+  ** have been deleted? This API will need to change to return an error code
+  ** as well as the boolean result value.
+  */
+  return (CURSOR_VALID!=pCur->eState);
 }
 
 /*
@@ -3222,8 +3416,21 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
   int rc;
   MemPage *pPage = pCur->pPage;
 
+#ifndef SQLITE_OMIT_SHARED_CACHE
+  rc = restoreCursorPosition(pCur, 1);
+  if( rc!=SQLITE_OK ){
+    return rc;
+  }
+  if( pCur->skip>0 ){
+    pCur->skip = 0;
+    *pRes = 0;
+    return SQLITE_OK;
+  }
+  pCur->skip = 0;
+#endif
+
   assert( pRes!=0 );
-  if( pCur->isValid==0 ){
+  if( CURSOR_INVALID==pCur->eState ){
     *pRes = 1;
     return SQLITE_OK;
   }
@@ -3243,7 +3450,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
     do{
       if( isRootPage(pPage) ){
         *pRes = 1;
-        pCur->isValid = 0;
+        pCur->eState = CURSOR_INVALID;
         return SQLITE_OK;
       }
       moveToParent(pCur);
@@ -3275,7 +3482,21 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
   int rc;
   Pgno pgno;
   MemPage *pPage;
-  if( pCur->isValid==0 ){
+
+#ifndef SQLITE_OMIT_SHARED_CACHE
+  rc = restoreCursorPosition(pCur, 1);
+  if( rc!=SQLITE_OK ){
+    return rc;
+  }
+  if( pCur->skip<0 ){
+    pCur->skip = 0;
+    *pRes = 0;
+    return SQLITE_OK;
+  }
+  pCur->skip = 0;
+#endif
+
+  if( CURSOR_INVALID==pCur->eState ){
     *pRes = 1;
     return SQLITE_OK;
   }
@@ -3291,7 +3512,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
   }else{
     while( pCur->idx==0 ){
       if( isRootPage(pPage) ){
-        pCur->isValid = 0;
+        pCur->eState = CURSOR_INVALID;
         *pRes = 1;
         return SQLITE_OK;
       }
@@ -4887,7 +5108,9 @@ static int balance(MemPage *pPage, int insert){
 static int checkReadLocks(BtShared *pBt, Pgno pgnoRoot, BtCursor *pExclude){
   BtCursor *p;
   for(p=pBt->pCursor; p; p=p->pNext){
+    u32 flags = (p->pBtree->pSqlite ? p->pBtree->pSqlite->flags : 0);
     if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue;
+    if( p->wrFlag==0 && flags&SQLITE_ReadUncommitted ) continue;
     if( p->wrFlag==0 ) return SQLITE_LOCKED;
     if( p->pPage->pgno!=p->pgnoRoot ){
       moveToRoot(p);
@@ -4929,6 +5152,14 @@ int sqlite3BtreeInsert(
   if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
     return SQLITE_LOCKED; /* The table pCur points to has a read lock */
   }
+
+  /* Save the positions of any other cursors open on this table */
+  restoreCursorPosition(pCur, 0);
+  rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
+  if( rc ){
+    return rc;
+  }
+
   rc = sqlite3BtreeMoveto(pCur, pKey, nKey, &loc);
   if( rc ) return rc;
   pPage = pCur->pPage;
@@ -4946,7 +5177,7 @@ int sqlite3BtreeInsert(
   if( rc ) goto end_insert;
   assert( szNew==cellSizePtr(pPage, newCell) );
   assert( szNew<=MX_CELL_SIZE(pBt) );
-  if( loc==0 && pCur->isValid ){
+  if( loc==0 && CURSOR_VALID==pCur->eState ){
     int szOld;
     assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
     oldCell = findCell(pPage, pCur->idx);
@@ -5003,8 +5234,19 @@ int sqlite3BtreeDelete(BtCursor *pCur){
   if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
     return SQLITE_LOCKED; /* The table pCur points to has a read lock */
   }
-  rc = sqlite3pager_write(pPage->aData);
-  if( rc ) return rc;
+
+  /* Restore the current cursor position (a no-op if the cursor is not in 
+  ** CURSOR_REQUIRESEEK state) and save the positions of any other cursors 
+  ** open on the same table. Then call sqlite3pager_write() on the page
+  ** that the entry will be deleted from.
+  */
+  if( 
+    (rc = restoreCursorPosition(pCur, 1)) ||
+    (rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) ||
+    (rc = sqlite3pager_write(pPage->aData))
+  ){
+    return rc;
+  }
 
   /* Locate the cell within it's page and leave pCell pointing to the
   ** data. The clearCell() call frees any overflow pages associated with the
@@ -5427,6 +5669,16 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
   unsigned char *pP1;
   BtShared *pBt = p->pBt;
 
+  /* Reading a meta-data value requires a read-lock on page 1 (and hence
+  ** the sqlite_master table. We grab this lock regardless of whether or
+  ** not the SQLITE_ReadUncommitted flag is set (the table rooted at page
+  ** 1 is treated as a special case by queryTableLock() and lockTable()).
+  */
+  rc = queryTableLock(p, 1, READ_LOCK);
+  if( rc!=SQLITE_OK ){
+    return rc;
+  }
+
   assert( idx>=0 && idx<=15 );
   rc = sqlite3pager_get(pBt->pPager, 1, (void**)&pP1);
   if( rc ) return rc;
@@ -5440,7 +5692,9 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
   if( idx==4 && *pMeta>0 ) pBt->readOnly = 1;
 #endif
 
-  return SQLITE_OK;
+  /* Grab the read-lock on page 1. */
+  rc = lockTable(p, 1, READ_LOCK);
+  return rc;
 }
 
 /*
@@ -5468,6 +5722,9 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
 ** is currently pointing to.
 */
 int sqlite3BtreeFlags(BtCursor *pCur){
+  /* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call
+  ** restoreCursorPosition() here.
+  */
   MemPage *pPage = pCur->pPage;
   return pPage ? pPage->aData[pPage->hdrOffset] : 0;
 }
@@ -5601,6 +5858,11 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
   MemPage *pPage = pCur->pPage;
   BtCursor tmpCur;
 
+  int rc = restoreCursorPosition(pCur, 1);
+  if( rc!=SQLITE_OK ){
+    return rc;
+  }
+
   pageIntegrity(pPage);
   assert( pPage->isInit );
   getTempCursor(pCur, &tmpCur);
@@ -6196,6 +6458,31 @@ int sqlite3BtreeSync(Btree *p, const char *zMaster){
   return SQLITE_OK;
 }
 
+/*
+** This function returns a pointer to a blob of memory associated with
+** a single shared-btree. The memory is used by client code for it's own
+** purposes (for example, to store a high-level schema associated with 
+** the shared-btree). The btree layer manages reference counting issues.
+**
+** The first time this is called on a shared-btree, nBytes bytes of memory
+** are allocated, zeroed, and returned to the caller. For each subsequent 
+** call the nBytes parameter is ignored and a pointer to the same blob
+** of memory returned. 
+**
+** Just before the shared-btree is closed, the function passed as the 
+** xFree argument when the memory allocation was made is invoked on the 
+** blob of allocated memory. This function should not call sqliteFree()
+** on the memory, the btree layer does that.
+*/
+void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
+  BtShared *pBt = p->pBt;
+  if( !pBt->pSchema ){
+    pBt->pSchema = sqliteMalloc(nBytes);
+    pBt->xFreeSchema = xFree;
+  }
+  return pBt->pSchema;
+}
+
 #ifndef SQLITE_OMIT_SHARED_CACHE
 /*
 ** Enable the shared pager and schema features.
index 75bb991a56bdb75bb4c50ee9da0a03efead3769c..69eb34fbf8e45f540efd0a220a5ad6283c7b35bd 100644 (file)
@@ -13,7 +13,7 @@
 ** subsystem.  See comments in the source code for a detailed description
 ** of what each interface routine does.
 **
-** @(#) $Id: btree.h,v 1.66 2005/12/30 16:28:02 danielk1977 Exp $
+** @(#) $Id: btree.h,v 1.67 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #ifndef _BTREE_H_
 #define _BTREE_H_
@@ -76,6 +76,7 @@ int sqlite3BtreeCreateTable(Btree*, int*, int flags);
 int sqlite3BtreeIsInTrans(Btree*);
 int sqlite3BtreeIsInStmt(Btree*);
 int sqlite3BtreeSync(Btree*, const char *zMaster);
+void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
 
 const char *sqlite3BtreeGetFilename(Btree *);
 const char *sqlite3BtreeGetDirname(Btree *);
index 8907fdf2f99b2b8b4b44a73b43eb5f48449a2431..3170bcf2d88d15672b2199bb97fc9728a140d43c 100644 (file)
@@ -22,7 +22,7 @@
 **     COMMIT
 **     ROLLBACK
 **
-** $Id: build.c,v 1.366 2006/01/04 21:40:07 drh Exp $
+** $Id: build.c,v 1.367 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -172,7 +172,7 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
   for(i=OMIT_TEMPDB; i<db->nDb; i++){
     int j = (i<2) ? i^1 : i;   /* Search TEMP before MAIN */
     if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue;
-    p = sqlite3HashFind(&db->aDb[j].tblHash, zName, strlen(zName)+1);
+    p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName, strlen(zName)+1);
     if( p ) break;
   }
   return p;
@@ -227,8 +227,12 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
   assert( (db->flags & SQLITE_Initialized) || db->init.busy );
   for(i=OMIT_TEMPDB; i<db->nDb; i++){
     int j = (i<2) ? i^1 : i;  /* Search TEMP before MAIN */
+    DbSchema *pSchema = db->aDb[j].pSchema;
     if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue;
-    p = sqlite3HashFind(&db->aDb[j].idxHash, zName, strlen(zName)+1);
+    assert( pSchema || (j==1 && !db->aDb[1].pBt) );
+    if( pSchema ){
+      p = sqlite3HashFind(&pSchema->idxHash, zName, strlen(zName)+1);
+    }
     if( p ) break;
   }
   return p;
@@ -252,10 +256,10 @@ static void freeIndex(Index *p){
 */
 static void sqliteDeleteIndex(sqlite3 *db, Index *p){
   Index *pOld;
+  const char *zName = p->zName;
 
-  assert( db!=0 && p->zName!=0 );
-  pOld = sqlite3HashInsert(&db->aDb[p->iDb].idxHash, p->zName,
-                          strlen(p->zName)+1, 0);
+  assert( db!=0 && zName!=0 );
+  pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName, strlen( zName)+1, 0);
   assert( pOld==0 || pOld==p );
   freeIndex(p);
 }
@@ -269,9 +273,10 @@ static void sqliteDeleteIndex(sqlite3 *db, Index *p){
 void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
   Index *pIndex;
   int len;
+  Hash *pHash = &db->aDb[iDb].pSchema->idxHash;
 
   len = strlen(zIdxName);
-  pIndex = sqlite3HashInsert(&db->aDb[iDb].idxHash, zIdxName, len+1, 0);
+  pIndex = sqlite3HashInsert(pHash, zIdxName, len+1, 0);
   if( pIndex ){
     if( pIndex->pTable->pIndex==pIndex ){
       pIndex->pTable->pIndex = pIndex->pNext;
@@ -308,23 +313,25 @@ void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
   db->flags &= ~SQLITE_Initialized;
   for(i=iDb; i<db->nDb; i++){
     Db *pDb = &db->aDb[i];
-    temp1 = pDb->tblHash;
-    temp2 = pDb->trigHash;
-    sqlite3HashInit(&pDb->trigHash, SQLITE_HASH_STRING, 0);
-    sqlite3HashClear(&pDb->aFKey);
-    sqlite3HashClear(&pDb->idxHash);
-    for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
-      sqlite3DeleteTrigger((Trigger*)sqliteHashData(pElem));
-    }
-    sqlite3HashClear(&temp2);
-    sqlite3HashInit(&pDb->tblHash, SQLITE_HASH_STRING, 0);
-    for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
-      Table *pTab = sqliteHashData(pElem);
-      sqlite3DeleteTable(db, pTab);
-    }
-    sqlite3HashClear(&temp1);
-    pDb->pSeqTab = 0;
-    DbClearProperty(db, i, DB_SchemaLoaded);
+    if( pDb->pSchema ){
+      temp1 = pDb->pSchema->tblHash;
+      temp2 = pDb->pSchema->trigHash;
+      sqlite3HashInit(&pDb->pSchema->trigHash, SQLITE_HASH_STRING, 0);
+      sqlite3HashClear(&pDb->pSchema->aFKey);
+      sqlite3HashClear(&pDb->pSchema->idxHash);
+      for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
+        sqlite3DeleteTrigger((Trigger*)sqliteHashData(pElem));
+      }
+      sqlite3HashClear(&temp2);
+      sqlite3HashInit(&pDb->pSchema->tblHash, SQLITE_HASH_STRING, 0);
+      for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
+        Table *pTab = sqliteHashData(pElem);
+        sqlite3DeleteTable(db, pTab);
+      }
+      sqlite3HashClear(&temp1);
+      pDb->pSchema->pSeqTab = 0;
+      DbClearProperty(db, i, DB_SchemaLoaded);
+    }
     if( iDb>0 ) return;
   }
   assert( iDb==0 );
@@ -433,7 +440,7 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
   */
   for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
     pNext = pIndex->pNext;
-    assert( pIndex->iDb==pTable->iDb || (pTable->iDb==0 && pIndex->iDb==1) );
+    assert( pIndex->pSchema==pTable->pSchema );
     sqliteDeleteIndex(db, pIndex);
   }
 
@@ -443,8 +450,8 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
   */
   for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){
     pNextFKey = pFKey->pNextFrom;
-    assert( pTable->iDb<db->nDb );
-    assert( sqlite3HashFind(&db->aDb[pTable->iDb].aFKey,
+    assert( sqlite3SchemaToIndex(db, pTable->pSchema)<db->nDb );
+    assert( sqlite3HashFind(&pTable->pSchema->aFKey,
                            pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey );
     sqliteFree(pFKey);
   }
@@ -475,14 +482,14 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){
   assert( iDb>=0 && iDb<db->nDb );
   assert( zTabName && zTabName[0] );
   pDb = &db->aDb[iDb];
-  p = sqlite3HashInsert(&pDb->tblHash, zTabName, strlen(zTabName)+1, 0);
+  p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, strlen(zTabName)+1,0);
   if( p ){
 #ifndef SQLITE_OMIT_FOREIGN_KEY
     for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){
       int nTo = strlen(pF1->zTo) + 1;
-      pF2 = sqlite3HashFind(&pDb->aFKey, pF1->zTo, nTo);
+      pF2 = sqlite3HashFind(&pDb->pSchema->aFKey, pF1->zTo, nTo);
       if( pF2==pF1 ){
-        sqlite3HashInsert(&pDb->aFKey, pF1->zTo, nTo, pF1->pNextTo);
+        sqlite3HashInsert(&pDb->pSchema->aFKey, pF1->zTo, nTo, pF1->pNextTo);
       }else{
         while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; }
         if( pF2 ){
@@ -734,7 +741,7 @@ void sqlite3StartTable(
   pTable->aCol = 0;
   pTable->iPKey = -1;
   pTable->pIndex = 0;
-  pTable->iDb = iDb;
+  pTable->pSchema = db->aDb[iDb].pSchema;
   pTable->nRef = 1;
   if( pParse->pNewTable ) sqlite3DeleteTable(db, pParse->pNewTable);
   pParse->pNewTable = pTable;
@@ -745,7 +752,7 @@ void sqlite3StartTable(
   */
 #ifndef SQLITE_OMIT_AUTOINCREMENT
   if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){
-    db->aDb[iDb].pSeqTab = pTable;
+    pTable->pSchema->pSeqTab = pTable;
   }
 #endif
 
@@ -1179,7 +1186,7 @@ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){
 ** 1 chance in 2^32.  So we're safe enough.
 */
 void sqlite3ChangeCookie(sqlite3 *db, Vdbe *v, int iDb){
-  sqlite3VdbeAddOp(v, OP_Integer, db->aDb[iDb].schema_cookie+1, 0);
+  sqlite3VdbeAddOp(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, 0);
   sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 0);
 }
 
@@ -1227,7 +1234,7 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){
 ** table.  Memory to hold the text of the statement is obtained
 ** from sqliteMalloc() and must be freed by the calling function.
 */
-static char *createTableStmt(Table *p){
+static char *createTableStmt(Table *p, int isTemp){
   int i, k, n;
   char *zStmt;
   char *zSep, *zSep2, *zEnd, *z;
@@ -1253,7 +1260,7 @@ static char *createTableStmt(Table *p){
   n += 35 + 6*p->nCol;
   zStmt = sqliteMallocRaw( n );
   if( zStmt==0 ) return 0;
-  strcpy(zStmt, !OMIT_TEMPDB&&p->iDb==1 ? "CREATE TEMP TABLE ":"CREATE TABLE ");
+  strcpy(zStmt, !OMIT_TEMPDB&&isTemp ? "CREATE TEMP TABLE ":"CREATE TABLE ");
   k = strlen(zStmt);
   identPut(zStmt, &k, p->zName);
   zStmt[k++] = '(';
@@ -1300,6 +1307,7 @@ void sqlite3EndTable(
 ){
   Table *p;
   sqlite3 *db = pParse->db;
+  int iDb;
 
   if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite3Tsd()->mallocFailed ) {
     return;
@@ -1309,6 +1317,8 @@ void sqlite3EndTable(
 
   assert( !db->init.busy || !pSelect );
 
+  iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
+
 #ifndef SQLITE_OMIT_CHECK
   /* Resolve names in all CHECK constraint expressions.
   */
@@ -1387,7 +1397,7 @@ void sqlite3EndTable(
     if( pSelect ){
       Table *pSelTab;
       sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
-      sqlite3VdbeAddOp(v, OP_Integer, p->iDb, 0);
+      sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
       sqlite3VdbeAddOp(v, OP_OpenWrite, 1, 0);
       pParse->nTab = 2;
       sqlite3Select(pParse, pSelect, SRT_Table, 1, 0, 0, 0, 0);
@@ -1406,7 +1416,7 @@ void sqlite3EndTable(
 
     /* Compute the complete text of the CREATE statement */
     if( pSelect ){
-      zStmt = createTableStmt(p);
+      zStmt = createTableStmt(p, p->pSchema==pParse->db->aDb[1].pSchema);
     }else{
       n = pEnd->z - pParse->sNameToken.z + 1;
       zStmt = sqlite3MPrintf("CREATE %s %.*s", zType2, n, pParse->sNameToken.z);
@@ -1422,22 +1432,22 @@ void sqlite3EndTable(
       "UPDATE %Q.%s "
          "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#0, sql=%Q "
        "WHERE rowid=#1",
-      db->aDb[p->iDb].zName, SCHEMA_TABLE(p->iDb),
+      db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
       zType,
       p->zName,
       p->zName,
       zStmt
     );
     sqliteFree(zStmt);
-    sqlite3ChangeCookie(db, v, p->iDb);
+    sqlite3ChangeCookie(db, v, iDb);
 
 #ifndef SQLITE_OMIT_AUTOINCREMENT
     /* Check to see if we need to create an sqlite_sequence table for
     ** keeping track of autoincrement keys.
     */
     if( p->autoInc ){
-      Db *pDb = &db->aDb[p->iDb];
-      if( pDb->pSeqTab==0 ){
+      Db *pDb = &db->aDb[iDb];
+      if( pDb->pSchema->pSeqTab==0 ){
         sqlite3NestedParse(pParse,
           "CREATE TABLE %Q.sqlite_sequence(name,seq)",
           pDb->zName
@@ -1447,7 +1457,7 @@ void sqlite3EndTable(
 #endif
 
     /* Reparse everything to update our internal data structures */
-    sqlite3VdbeOp3(v, OP_ParseSchema, p->iDb, 0,
+    sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0,
         sqlite3MPrintf("tbl_name='%q'",p->zName), P3_DYNAMIC);
   }
 
@@ -1457,8 +1467,8 @@ void sqlite3EndTable(
   if( db->init.busy && pParse->nErr==0 ){
     Table *pOld;
     FKey *pFKey; 
-    Db *pDb = &db->aDb[p->iDb];
-    pOld = sqlite3HashInsert(&pDb->tblHash, p->zName, strlen(p->zName)+1, p);
+    DbSchema *pSchema = p->pSchema;
+    pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, strlen(p->zName)+1,p);
     if( pOld ){
       assert( p==pOld );  /* Malloc must have failed inside HashInsert() */
       return;
@@ -1466,8 +1476,8 @@ void sqlite3EndTable(
 #ifndef SQLITE_OMIT_FOREIGN_KEY
     for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){
       int nTo = strlen(pFKey->zTo) + 1;
-      pFKey->pNextTo = sqlite3HashFind(&pDb->aFKey, pFKey->zTo, nTo);
-      sqlite3HashInsert(&pDb->aFKey, pFKey->zTo, nTo, pFKey);
+      pFKey->pNextTo = sqlite3HashFind(&pSchema->aFKey, pFKey->zTo, nTo);
+      sqlite3HashInsert(&pSchema->aFKey, pFKey->zTo, nTo, pFKey);
     }
 #endif
     pParse->pNewTable = 0;
@@ -1502,6 +1512,7 @@ void sqlite3CreateView(
   Token sEnd;
   DbFixer sFix;
   Token *pName;
+  int iDb;
 
   if( pParse->nVar>0 ){
     sqlite3ErrorMsg(pParse, "parameters are not allowed in views");
@@ -1515,7 +1526,8 @@ void sqlite3CreateView(
     return;
   }
   sqlite3TwoPartName(pParse, pName1, pName2, &pName);
-  if( sqlite3FixInit(&sFix, pParse, p->iDb, "view", pName)
+  iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
+  if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName)
     && sqlite3FixSelect(&sFix, pSelect)
   ){
     sqlite3SelectDelete(pSelect);
@@ -1615,7 +1627,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
       pSelTab->nCol = 0;
       pSelTab->aCol = 0;
       sqlite3DeleteTable(0, pSelTab);
-      DbSetProperty(pParse->db, pTable->iDb, DB_UnresetViews);
+      pTable->pSchema->flags |= DB_UnresetViews;
     }else{
       pTable->nCol = 0;
       nErr++;
@@ -1635,7 +1647,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
 static void sqliteViewResetAll(sqlite3 *db, int idx){
   HashElem *i;
   if( !DbHasProperty(db, idx, DB_UnresetViews) ) return;
-  for(i=sqliteHashFirst(&db->aDb[idx].tblHash); i; i=sqliteHashNext(i)){
+  for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){
     Table *pTab = sqliteHashData(i);
     if( pTab->pSelect ){
       sqliteResetColumnNames(pTab);
@@ -1656,15 +1668,18 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
 #ifndef SQLITE_OMIT_AUTOVACUUM
 void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){
   HashElem *pElem;
-  
-  for(pElem=sqliteHashFirst(&pDb->tblHash); pElem; pElem=sqliteHashNext(pElem)){
+  Hash *pHash;
+
+  pHash = &pDb->pSchema->tblHash;
+  for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){
     Table *pTab = sqliteHashData(pElem);
     if( pTab->tnum==iFrom ){
       pTab->tnum = iTo;
       return;
     }
   }
-  for(pElem=sqliteHashFirst(&pDb->idxHash); pElem; pElem=sqliteHashNext(pElem)){
+  pHash = &pDb->pSchema->idxHash;
+  for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){
     Index *pIdx = sqliteHashData(pElem);
     if( pIdx->tnum==iFrom ){
       pIdx->tnum = iTo;
@@ -1741,14 +1756,18 @@ static void destroyTable(Parse *pParse, Table *pTab){
     }
     for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
       int iIdx = pIdx->tnum;
-      assert( pIdx->iDb==pTab->iDb );
+      assert( pIdx->pSchema==pTab->pSchema );
       if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){
         iLargest = iIdx;
       }
     }
-    if( iLargest==0 ) return;
-    destroyRootPage(pParse, iLargest, pTab->iDb);
-    iDestroyed = iLargest;
+    if( iLargest==0 ){
+      return;
+    }else{
+      int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+      destroyRootPage(pParse, iLargest, iDb);
+      iDestroyed = iLargest;
+    }
   }
 #endif
 }
@@ -1773,13 +1792,13 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
     }
     goto exit_drop_table;
   }
-  iDb = pTab->iDb;
+  iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
   assert( iDb>=0 && iDb<db->nDb );
 #ifndef SQLITE_OMIT_AUTHORIZATION
   {
     int code;
-    const char *zTab = SCHEMA_TABLE(pTab->iDb);
-    const char *zDb = db->aDb[pTab->iDb].zName;
+    const char *zTab = SCHEMA_TABLE(iDb);
+    const char *zDb = db->aDb[iDb].zName;
     if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
       goto exit_drop_table;
     }
@@ -1804,7 +1823,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
     }
   }
 #endif
-  if( pTab->readOnly || pTab==db->aDb[iDb].pSeqTab ){
+  if( pTab->readOnly || pTab==db->aDb[iDb].pSchema->pSeqTab ){
     sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
     goto exit_drop_table;
   }
@@ -1829,7 +1848,6 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
   v = sqlite3GetVdbe(pParse);
   if( v ){
     Trigger *pTrigger;
-    int iDb = pTab->iDb;
     Db *pDb = &db->aDb[iDb];
     sqlite3BeginWriteOperation(pParse, 0, iDb);
 
@@ -1839,7 +1857,8 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
     */
     pTrigger = pTab->pTrigger;
     while( pTrigger ){
-      assert( pTrigger->iDb==iDb || pTrigger->iDb==1 );
+      assert( pTrigger->pSchema==pTab->pSchema || 
+          pTrigger->pSchema==db->aDb[1].pSchema );
       sqlite3DropTriggerPtr(pParse, pTrigger, 1);
       pTrigger = pTrigger->pNext;
     }
@@ -2035,10 +2054,11 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
   int addr1;                     /* Address of top of loop */
   int tnum;                      /* Root page of index */
   Vdbe *v;                       /* Generate code into this virtual machine */
+  int iDb = sqlite3SchemaToIndex(pParse->db, pIndex->pSchema);
 
 #ifndef SQLITE_OMIT_AUTHORIZATION
   if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0,
-      pParse->db->aDb[pIndex->iDb].zName ) ){
+      pParse->db->aDb[iDb].zName ) ){
     return;
   }
 #endif
@@ -2058,12 +2078,12 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
     tnum = 0;
   }else{
     tnum = pIndex->tnum;
-    sqlite3VdbeAddOp(v, OP_Clear, tnum, pIndex->iDb);
+    sqlite3VdbeAddOp(v, OP_Clear, tnum, iDb);
   }
-  sqlite3VdbeAddOp(v, OP_Integer, pIndex->iDb, 0);
+  sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
   sqlite3VdbeOp3(v, OP_OpenWrite, iIdx, tnum,
                     (char*)&pIndex->keyInfo, P3_KEYINFO);
-  sqlite3OpenTableForReading(v, iTab, pTab);
+  sqlite3OpenTableForReading(v, iTab, iDb, pTab);
   addr1 = sqlite3VdbeAddOp(v, OP_Rewind, iTab, 0);
   sqlite3GenerateIndexKey(v, pIndex, iTab);
   if( pIndex->onError!=OE_None ){
@@ -2142,7 +2162,7 @@ void sqlite3CreateIndex(
     ** is a temp table. If so, set the database to 1.
     */
     pTab = sqlite3SrcListLookup(pParse, pTblName);
-    if( pName2 && pName2->n==0 && pTab && pTab->iDb==1 ){
+    if( pName2 && pName2->n==0 && pTab && pTab->pSchema==db->aDb[1].pSchema ){
       iDb = 1;
     }
 #endif
@@ -2157,12 +2177,12 @@ void sqlite3CreateIndex(
     pTab = sqlite3LocateTable(pParse, pTblName->a[0].zName, 
         pTblName->a[0].zDatabase);
     if( !pTab ) goto exit_create_index;
-    assert( iDb==pTab->iDb );
+    assert( db->aDb[iDb].pSchema==pTab->pSchema );
   }else{
     assert( pName==0 );
-    pTab =  pParse->pNewTable;
+    pTab = pParse->pNewTable;
     if( !pTab ) goto exit_create_index;
-    iDb = pTab->iDb;
+    iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
   }
   pDb = &db->aDb[iDb];
 
@@ -2266,11 +2286,11 @@ void sqlite3CreateIndex(
   pIndex->nColumn = pList->nExpr;
   pIndex->onError = onError;
   pIndex->autoIndex = pName==0;
-  pIndex->iDb = iDb;
+  pIndex->pSchema = db->aDb[iDb].pSchema;
 
   /* Check to see if we should honor DESC requests on index columns
   */
-  if( pDb->file_format>=4 ){
+  if( pDb->pSchema->file_format>=4 ){
     sortOrderMask = -1;   /* Honor DESC */
   }else{
     sortOrderMask = 0;    /* Ignore DESC */
@@ -2365,7 +2385,7 @@ void sqlite3CreateIndex(
   */
   if( db->init.busy ){
     Index *p;
-    p = sqlite3HashInsert(&db->aDb[pIndex->iDb].idxHash, 
+    p = sqlite3HashInsert(&pIndex->pSchema->idxHash, 
                          pIndex->zName, strlen(pIndex->zName)+1, pIndex);
     if( p ){
       assert( p==pIndex );  /* Malloc must have failed */
@@ -2533,6 +2553,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
   Index *pIndex;
   Vdbe *v;
   sqlite3 *db = pParse->db;
+  int iDb;
 
   if( pParse->nErr || sqlite3Tsd()->mallocFailed ){
     goto exit_drop_index;
@@ -2554,16 +2575,17 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
       "or PRIMARY KEY constraint cannot be dropped", 0);
     goto exit_drop_index;
   }
+  iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
 #ifndef SQLITE_OMIT_AUTHORIZATION
   {
     int code = SQLITE_DROP_INDEX;
     Table *pTab = pIndex->pTable;
-    const char *zDb = db->aDb[pIndex->iDb].zName;
-    const char *zTab = SCHEMA_TABLE(pIndex->iDb);
+    const char *zDb = db->aDb[iDb].zName;
+    const char *zTab = SCHEMA_TABLE(iDb);
     if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
       goto exit_drop_index;
     }
-    if( !OMIT_TEMPDB && pIndex->iDb ) code = SQLITE_DROP_TEMP_INDEX;
+    if( !OMIT_TEMPDB && iDb ) code = SQLITE_DROP_TEMP_INDEX;
     if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){
       goto exit_drop_index;
     }
@@ -2573,7 +2595,6 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
   /* Generate code to remove the index and from the master table */
   v = sqlite3GetVdbe(pParse);
   if( v ){
-    int iDb = pIndex->iDb;
     sqlite3NestedParse(pParse,
        "DELETE FROM %Q.%s WHERE name=%Q",
        db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
@@ -2853,6 +2874,12 @@ static int sqlite3OpenTempDatabase(Parse *pParse){
       pParse->rc = rc;
       return 1;
     }
+/*
+    db->aDb[1].pSchema = sqlite3SchemaGet(db->aDb[1].pBt);
+    if( !db->aDb[1].pSchema ){
+      return SQLITE_NOMEM;
+    }
+*/
     if( db->flags & !db->autoCommit ){
       rc = sqlite3BtreeBeginTrans(db->aDb[1].pBt, 1);
       if( rc!=SQLITE_OK ){
@@ -2906,7 +2933,7 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
     mask = 1<<iDb;
     if( (pParse->cookieMask & mask)==0 ){
       pParse->cookieMask |= mask;
-      pParse->cookieValue[iDb] = db->aDb[iDb].schema_cookie;
+      pParse->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
       if( !OMIT_TEMPDB && iDb==1 ){
         sqlite3OpenTempDatabase(pParse);
       }
@@ -2971,7 +2998,8 @@ static void reindexTable(Parse *pParse, Table *pTab, CollSeq *pColl){
 
   for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){
     if( pColl==0 || collationMatch(pColl,pIndex) ){
-      sqlite3BeginWriteOperation(pParse, 0, pTab->iDb);
+      int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+      sqlite3BeginWriteOperation(pParse, 0, iDb);
       sqlite3RefillIndex(pParse, pIndex, -1);
     }
   }
@@ -2993,7 +3021,7 @@ static void reindexDatabases(Parse *pParse, CollSeq *pColl){
 
   for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){
     if( pDb==0 ) continue;
-    for(k=sqliteHashFirst(&pDb->tblHash);  k; k=sqliteHashNext(k)){
+    for(k=sqliteHashFirst(&pDb->pSchema->tblHash);  k; k=sqliteHashNext(k)){
       pTab = (Table*)sqliteHashData(k);
       reindexTable(pParse, pTab, pColl);
     }
index 259e0f5a8ad06191c617afc6779d00a099c0e145..9114304edbc4ce416571ec01e70912ad304ee7b8 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** in order to generate code for DELETE FROM statements.
 **
-** $Id: delete.c,v 1.113 2005/12/15 15:22:09 danielk1977 Exp $
+** $Id: delete.c,v 1.114 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -62,9 +62,10 @@ int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
 void sqlite3OpenTableForReading(
   Vdbe *v,        /* Generate code into this VDBE */
   int iCur,       /* The cursor number of the table */
+  int iDb,        /* The database index in sqlite3.aDb[] */
   Table *pTab     /* The table to be opened */
 ){
-  sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+  sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
   VdbeComment((v, "# %s", pTab->zName));
   sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
   sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
@@ -95,6 +96,7 @@ void sqlite3DeleteFrom(
   AuthContext sContext;  /* Authorization context */
   int oldIdx = -1;       /* Cursor for the OLD table of AFTER triggers */
   NameContext sNC;       /* Name context to resolve expressions in */
+  int iDb;
 
 #ifndef SQLITE_OMIT_TRIGGER
   int isView;                  /* True if attempting to delete from a view */
@@ -134,8 +136,9 @@ void sqlite3DeleteFrom(
   if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){
     goto delete_from_cleanup;
   }
-  assert( pTab->iDb<db->nDb );
-  zDb = db->aDb[pTab->iDb].zName;
+  iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+  assert( iDb<db->nDb );
+  zDb = db->aDb[iDb].zName;
   if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
     goto delete_from_cleanup;
   }
@@ -176,7 +179,7 @@ void sqlite3DeleteFrom(
     goto delete_from_cleanup;
   }
   if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
-  sqlite3BeginWriteOperation(pParse, triggers_exist, pTab->iDb);
+  sqlite3BeginWriteOperation(pParse, triggers_exist, iDb);
 
   /* If we are trying to delete from a view, realize that view into
   ** a ephemeral table.
@@ -205,7 +208,7 @@ void sqlite3DeleteFrom(
       int endOfLoop = sqlite3VdbeMakeLabel(v);
       int addr;
       if( !isView ){
-        sqlite3OpenTableForReading(v, iCur, pTab);
+        sqlite3OpenTableForReading(v, iCur, iDb, pTab);
       }
       sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2);
       addr = sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
@@ -214,12 +217,13 @@ void sqlite3DeleteFrom(
       sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
     }
     if( !isView ){
-      sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb);
+      sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, iDb);
       if( !pParse->nested ){
         sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
       }
       for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
-        sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb);
+        assert( pIdx->pSchema==pTab->pSchema );
+        sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, iDb);
       }
     }
   }
@@ -272,7 +276,7 @@ void sqlite3DeleteFrom(
       addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
       if( !isView ){
         sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
-        sqlite3OpenTableForReading(v, iCur, pTab);
+        sqlite3OpenTableForReading(v, iCur, iDb, pTab);
       }
       sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
       sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
index d56b81943547cfbe3ab07146a9e617fc9dd307dd..2846017dbbcda406c2ffa73d9a91f7eacab4f21e 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains routines used for analyzing expressions and
 ** for generating VDBE code that evaluates expressions in SQLite.
 **
-** $Id: expr.c,v 1.243 2006/01/03 15:16:26 drh Exp $
+** $Id: expr.c,v 1.244 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -843,6 +843,7 @@ static int lookupName(
     if( pSrcList ){
       for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
         Table *pTab = pItem->pTab;
+        int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
         Column *pCol;
   
         if( pTab==0 ) continue;
@@ -854,14 +855,14 @@ static int lookupName(
           }else{
             char *zTabName = pTab->zName;
             if( zTabName==0 || sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
-            if( zDb!=0 && sqlite3StrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){
+            if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){
               continue;
             }
           }
         }
         if( 0==(cntTab++) ){
           pExpr->iTable = pItem->iCursor;
-          pExpr->iDb = pTab->iDb;
+          pExpr->pSchema = pTab->pSchema;
           pMatch = pItem;
         }
         for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
@@ -870,7 +871,7 @@ static int lookupName(
             cnt++;
             pExpr->iTable = pItem->iCursor;
             pMatch = pItem;
-            pExpr->iDb = pTab->iDb;
+            pExpr->pSchema = pTab->pSchema;
             /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
             pExpr->iColumn = j==pTab->iPKey ? -1 : j;
             pExpr->affinity = pTab->aCol[j].affinity;
@@ -921,7 +922,7 @@ static int lookupName(
         int j;
         Column *pCol = pTab->aCol;
 
-        pExpr->iDb = pTab->iDb;
+        pExpr->pSchema = pTab->pSchema;
         cntTab++;
         for(j=0; j < pTab->nCol; j++, pCol++) {
           if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
index bf7c594d3c3ef6c77a365fdb93863e6beb1b5334..64bff8d5cf57a589d235a0aaf1a53f984017b95c 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.151 2005/12/15 15:22:09 danielk1977 Exp $
+** $Id: insert.c,v 1.152 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -104,15 +104,15 @@ void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){
 **
 ** No checking is done for sub-selects that are part of expressions.
 */
-static int selectReadsTable(Select *p, int iDb, int iTab){
+static int selectReadsTable(Select *p, DbSchema *pSchema, int iTab){
   int i;
   struct SrcList_item *pItem;
   if( p->pSrc==0 ) return 0;
   for(i=0, pItem=p->pSrc->a; i<p->pSrc->nSrc; i++, pItem++){
     if( pItem->pSelect ){
-      if( selectReadsTable(pItem->pSelect, iDb, iTab) ) return 1;
+      if( selectReadsTable(pItem->pSelect, pSchema, iTab) ) return 1;
     }else{
-      if( pItem->pTab->iDb==iDb && pItem->pTab->tnum==iTab ) return 1;
+      if( pItem->pTab->pSchema==pSchema && pItem->pTab->tnum==iTab ) return 1;
     }
   }
   return 0;
@@ -214,6 +214,7 @@ void sqlite3Insert(
   int newIdx = -1;      /* Cursor for the NEW table */
   Db *pDb;              /* The database containing table being inserted into */
   int counterMem = 0;   /* Memory cell holding AUTOINCREMENT counter */
+  int iDb;
 
 #ifndef SQLITE_OMIT_TRIGGER
   int isView;                 /* True if attempting to insert into a view */
@@ -236,8 +237,9 @@ void sqlite3Insert(
   if( pTab==0 ){
     goto insert_cleanup;
   }
-  assert( pTab->iDb<db->nDb );
-  pDb = &db->aDb[pTab->iDb];
+  iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+  assert( iDb<db->nDb );
+  pDb = &db->aDb[iDb];
   zDb = pDb->zName;
   if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
     goto insert_cleanup;
@@ -285,7 +287,7 @@ void sqlite3Insert(
   v = sqlite3GetVdbe(pParse);
   if( v==0 ) goto insert_cleanup;
   if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
-  sqlite3BeginWriteOperation(pParse, pSelect || triggers_exist, pTab->iDb);
+  sqlite3BeginWriteOperation(pParse, pSelect || triggers_exist, iDb);
 
   /* if there are row triggers, allocate a temp table for new.* references. */
   if( triggers_exist ){
@@ -303,8 +305,8 @@ void sqlite3Insert(
     int base = sqlite3VdbeCurrentAddr(v);
     counterRowid = pParse->nMem++;
     counterMem = pParse->nMem++;
-    sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
-    sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pDb->pSeqTab->tnum);
+    sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
+    sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pDb->pSchema->pSeqTab->tnum);
     sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, 2);
     sqlite3VdbeAddOp(v, OP_Rewind, iCur, base+13);
     sqlite3VdbeAddOp(v, OP_Column, iCur, 0);
@@ -353,7 +355,7 @@ void sqlite3Insert(
     ** of the tables being read by the SELECT statement.  Also use a 
     ** temp table in the case of row triggers.
     */
-    if( triggers_exist || selectReadsTable(pSelect, pTab->iDb, pTab->tnum) ){
+    if( triggers_exist || selectReadsTable(pSelect,pTab->pSchema,pTab->tnum) ){
       useTempTable = 1;
     }
 
@@ -684,8 +686,8 @@ void sqlite3Insert(
   if( pTab->autoInc ){
     int iCur = pParse->nTab;
     int base = sqlite3VdbeCurrentAddr(v);
-    sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
-    sqlite3VdbeAddOp(v, OP_OpenWrite, iCur, pDb->pSeqTab->tnum);
+    sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
+    sqlite3VdbeAddOp(v, OP_OpenWrite, iCur, pDb->pSchema->pSeqTab->tnum);
     sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, 2);
     sqlite3VdbeAddOp(v, OP_MemLoad, counterRowid, 0);
     sqlite3VdbeAddOp(v, OP_NotNull, -1, base+7);
@@ -1104,15 +1106,17 @@ void sqlite3OpenTableAndIndices(
   int op           /* OP_OpenRead or OP_OpenWrite */
 ){
   int i;
+  int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
   Index *pIdx;
   Vdbe *v = sqlite3GetVdbe(pParse);
   assert( v!=0 );
-  sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+  sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
   VdbeComment((v, "# %s", pTab->zName));
   sqlite3VdbeAddOp(v, op, base, pTab->tnum);
   sqlite3VdbeAddOp(v, OP_SetNumColumns, base, pTab->nCol);
   for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
-    sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+    assert( pIdx->pSchema==pTab->pSchema );
+    sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
     VdbeComment((v, "# %s", pIdx->zName));
     sqlite3VdbeOp3(v, op, i+base, pIdx->tnum,
                    (char*)&pIdx->keyInfo, P3_KEYINFO);
index cb03503806fb78686989896b96b7885b6017717c..9858e6efc00173a4169f5db1fd854d1f86ebc51a 100644 (file)
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.313 2005/12/30 16:28:02 danielk1977 Exp $
+** $Id: main.c,v 1.314 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -95,6 +95,53 @@ int sqlite3_total_changes(sqlite3 *db){
   return db->nTotalChange;
 }
 
+/*
+** Free a schema structure.
+*/
+void sqlite3SchemaFree(void *p){
+  sqliteFree(p);
+}
+
+DbSchema *sqlite3SchemaGet(Btree *pBt){
+  DbSchema * p;
+  if( pBt ){
+    p = (DbSchema *)sqlite3BtreeSchema(pBt,sizeof(DbSchema),sqlite3SchemaFree);
+  }else{
+    p = (DbSchema *)sqliteMalloc(sizeof(DbSchema));
+  }
+  if( p ){
+    sqlite3HashInit(&p->tblHash, SQLITE_HASH_STRING, 0);
+    sqlite3HashInit(&p->idxHash, SQLITE_HASH_STRING, 0);
+    sqlite3HashInit(&p->trigHash, SQLITE_HASH_STRING, 0);
+    sqlite3HashInit(&p->aFKey, SQLITE_HASH_STRING, 1);
+  }
+  return p;
+}
+
+int sqlite3SchemaToIndex(sqlite3 *db, DbSchema *pSchema){
+  int i = -1000000;
+
+  /* If pSchema is NULL, then return -1000000. This happens when code in 
+  ** expr.c is trying to resolve a reference to a transient table (i.e. one
+  ** created by a sub-select). In this case the return value of this 
+  ** function should never be used.
+  **
+  ** We return -1000000 instead of the more usual -1 simply because using
+  ** -1000000 as incorrectly using -1000000 index into db->aDb[] is much 
+  ** more likely to cause a segfault than -1 (of course there are assert()
+  ** statements too, but it never hurts to play the odds).
+  */
+  if( pSchema ){
+    for(i=0; i<db->nDb; i++){
+      if( db->aDb[i].pSchema==pSchema ){
+        break;
+      }
+    }
+    assert( i>=0 &&i>=0 &&  i<db->nDb );
+  }
+  return i;
+}
+
 /*
 ** Close an existing SQLite database
 */
@@ -185,6 +232,7 @@ int sqlite3_close(sqlite3 *db){
 #endif
 
   db->magic = SQLITE_MAGIC_ERROR;
+  sqliteFree(db->aDb[1].pSchema);
   sqliteFree(db);
   sqlite3MallocAllow();
   return SQLITE_OK;
@@ -639,7 +687,7 @@ int sqlite3BtreeFactory(
 #endif /* SQLITE_OMIT_MEMORYDB */
   }
 
-  rc = sqlite3BtreeOpen(zFilename, db, ppBtree, btree_flags);
+  rc = sqlite3BtreeOpen(zFilename, (sqlite3 *)db, ppBtree, btree_flags);
   if( rc==SQLITE_OK ){
     sqlite3BtreeSetBusyHandler(*ppBtree, (void*)&db->busyHandler);
     sqlite3BtreeSetCacheSize(*ppBtree, nCache);
@@ -732,7 +780,7 @@ static int openDatabase(
   sqlite3 **ppDb         /* OUT: Returned database handle */
 ){
   sqlite3 *db;
-  int rc, i;
+  int rc;
   CollSeq *pColl;
 
   assert( !sqlite3Tsd()->mallocFailed );
@@ -749,12 +797,15 @@ static int openDatabase(
   db->flags |= SQLITE_ShortColNames;
   sqlite3HashInit(&db->aFunc, SQLITE_HASH_STRING, 0);
   sqlite3HashInit(&db->aCollSeq, SQLITE_HASH_STRING, 0);
+
+#if 0
   for(i=0; i<db->nDb; i++){
     sqlite3HashInit(&db->aDb[i].tblHash, SQLITE_HASH_STRING, 0);
     sqlite3HashInit(&db->aDb[i].idxHash, SQLITE_HASH_STRING, 0);
     sqlite3HashInit(&db->aDb[i].trigHash, SQLITE_HASH_STRING, 0);
     sqlite3HashInit(&db->aDb[i].aFKey, SQLITE_HASH_STRING, 1);
   }
+#endif
   
   /* Add the default collation sequence BINARY. BINARY works for both UTF-8
   ** and UTF-16, so add a version for each to avoid any unnecessary
@@ -789,6 +840,8 @@ static int openDatabase(
     db->magic = SQLITE_MAGIC_CLOSED;
     goto opendb_out;
   }
+  db->aDb[0].pSchema = sqlite3SchemaGet(db->aDb[0].pBt);
+  db->aDb[1].pSchema = sqlite3SchemaGet(0);
 
   /* The default safety_level for the main database is 'full'; for the temp
   ** database it is 'NONE'. This matches the pager layer defaults.  
@@ -800,7 +853,6 @@ static int openDatabase(
   db->aDb[1].safety_level = 1;
 #endif
 
-
   /* Register all built-in functions, but do not attempt to read the
   ** database schema yet. This is delayed until the first time the database
   ** is accessed.
index dd1485a88577cc900d2e4aab200212e71cb2796a..413ac8b23d906d9a1fcb34dd22b89d2d9a1ef09f 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to implement the PRAGMA command.
 **
-** $Id: pragma.c,v 1.107 2005/12/09 20:02:05 drh Exp $
+** $Id: pragma.c,v 1.108 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -157,6 +157,10 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
     /* The following is VERY experimental */
     { "writable_schema",          SQLITE_WriteSchema   },
     { "omit_readlock",            SQLITE_NoReadlock    },
+
+    /* TODO: Maybe it shouldn't be possible to change the ReadUncommitted
+    ** flag if there are any active statements. */
+    { "read_uncommitted",         SQLITE_ReadUncommitted },
   };
   int i;
   const struct sPragmaType *p;
@@ -650,6 +654,7 @@ void sqlite3Pragma(
     /* Do an integrity check on each database file */
     for(i=0; i<db->nDb; i++){
       HashElem *x;
+      Hash *pTbls;
       int cnt = 0;
 
       if( OMIT_TEMPDB && i==1 ) continue;
@@ -658,7 +663,8 @@ void sqlite3Pragma(
 
       /* Do an integrity check of the B-Tree
       */
-      for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){
+      pTbls = &db->aDb[i].pSchema->tblHash;
+      for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
         Table *pTab = sqliteHashData(x);
         Index *pIdx;
         sqlite3VdbeAddOp(v, OP_Integer, pTab->tnum, 0);
@@ -685,7 +691,7 @@ void sqlite3Pragma(
       /* Make sure all the indices are constructed correctly.
       */
       sqlite3CodeVerifySchema(pParse, i);
-      for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){
+      for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
         Table *pTab = sqliteHashData(x);
         Index *pIdx;
         int loopTop;
index 1510b344e208c55e3a11c70d3347d6b3bc5115bf..6a75aa2ffdd2bababd1323f33b0141c65eb3b352 100644 (file)
@@ -13,7 +13,7 @@
 ** interface, and routines that contribute to loading the database schema
 ** from disk.
 **
-** $Id: prepare.c,v 1.12 2006/01/04 15:54:36 drh Exp $
+** $Id: prepare.c,v 1.13 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -49,6 +49,10 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){
   sqlite3 *db = pData->db;
   int iDb;
 
+  if( sqlite3Tsd()->mallocFailed ){
+    return SQLITE_NOMEM;
+  }
+
   assert( argc==4 );
   if( argv==0 ) return 0;   /* Might happen if EMPTY_RESULT_CALLBACKS are on */
   if( argv[1]==0 || argv[3]==0 ){
@@ -151,6 +155,22 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
 
   assert( iDb>=0 && iDb<db->nDb );
 
+  if( 0==db->aDb[iDb].pSchema ){
+    DbSchema *pS = sqlite3BtreeSchema(db->aDb[iDb].pBt, sizeof(DbSchema), 
+        sqlite3SchemaFree);
+    db->aDb[iDb].pSchema = pS;
+    if( !pS ){
+      return SQLITE_NOMEM;
+    }else if( pS->file_format!=0 ){
+      return SQLITE_OK;
+    }else{
+      sqlite3HashInit(&pS->tblHash, SQLITE_HASH_STRING, 0);
+      sqlite3HashInit(&pS->idxHash, SQLITE_HASH_STRING, 0);
+      sqlite3HashInit(&pS->trigHash, SQLITE_HASH_STRING, 0);
+      sqlite3HashInit(&pS->aFKey, SQLITE_HASH_STRING, 1);
+    }
+  }
+
   /* zMasterSchema and zInitScript are set to point at the master schema
   ** and initialisation script appropriate for the database being
   ** initialised. zMasterName is the name of the master table.
@@ -226,7 +246,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
   }else{
     memset(meta, 0, sizeof(meta));
   }
-  pDb->schema_cookie = meta[0];
+  pDb->pSchema->schema_cookie = meta[0];
 
   /* If opening a non-empty database, check the text encoding. For the
   ** main database, set sqlite3.enc to the encoding of the main database.
@@ -260,11 +280,11 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
   ** file_format==3    Version 3.1.4.  // ditto but with non-NULL defaults
   ** file_format==4    Version 3.3.0.  // DESC indices.  Boolean constants
   */
-  pDb->file_format = meta[1];
-  if( pDb->file_format==0 ){
-    pDb->file_format = 1;
+  pDb->pSchema->file_format = meta[1];
+  if( pDb->pSchema->file_format==0 ){
+    pDb->pSchema->file_format = 1;
   }
-  if( pDb->file_format>SQLITE_MAX_FILE_FORMAT ){
+  if( pDb->pSchema->file_format>SQLITE_MAX_FILE_FORMAT ){
     sqlite3BtreeCloseCursor(curMain);
     sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
     return SQLITE_ERROR;
@@ -394,7 +414,7 @@ static int schemaIsValid(sqlite3 *db){
     rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, 0, &curTemp);
     if( rc==SQLITE_OK ){
       rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&cookie);
-      if( rc==SQLITE_OK && cookie!=db->aDb[iDb].schema_cookie ){
+      if( rc==SQLITE_OK && cookie!=db->aDb[iDb].pSchema->schema_cookie ){
         allOk = 0;
       }
       sqlite3BtreeCloseCursor(curTemp);
index 298d0f839dc310e2debdb028fc8be7859548fdfa..20f07d4db036ec362d3653c46ae6e8ad636abdd9 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.283 2006/01/03 15:16:26 drh Exp $
+** $Id: select.c,v 1.284 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -2202,6 +2202,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
   struct ExprList_item eListItem;
   SrcList *pSrc;
   int brk;
+  int iDb;
 
   /* Check to see if this query is a simple min() or max() query.  Return
   ** zero if it is  not.
@@ -2263,12 +2264,13 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
   ** the min() or max() is on the INTEGER PRIMARY KEY, then find the first
   ** or last entry in the main table.
   */
-  sqlite3CodeVerifySchema(pParse, pTab->iDb);
+  iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+  sqlite3CodeVerifySchema(pParse, iDb);
   base = pSrc->a[0].iCursor;
   brk = sqlite3VdbeMakeLabel(v);
   computeLimitRegisters(pParse, p, brk);
   if( pSrc->a[0].pSelect==0 ){
-    sqlite3OpenTableForReading(v, base, pTab);
+    sqlite3OpenTableForReading(v, base, iDb, pTab);
   }
   if( pIdx==0 ){
     sqlite3VdbeAddOp(v, seekOp, base, 0);
@@ -2281,7 +2283,8 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
     */
     int iIdx;
     iIdx = pParse->nTab++;
-    sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+    assert( pIdx->pSchema==pTab->pSchema );
+    sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
     sqlite3VdbeOp3(v, OP_OpenRead, iIdx, pIdx->tnum,
                    (char*)&pIdx->keyInfo, P3_KEYINFO);
     if( seekOp==OP_Rewind ){
index 7c30a5eeccbe72920c6d9b58d52622c30ecbb1f1..3cc6d6fdfb5d1c30c925caea10ce29b63544623a 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.447 2006/01/04 15:54:36 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.448 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -336,6 +336,7 @@ typedef struct AuthContext AuthContext;
 typedef struct CollSeq CollSeq;
 typedef struct Column Column;
 typedef struct Db Db;
+typedef struct DbSchema DbSchema;
 typedef struct Expr Expr;
 typedef struct ExprList ExprList;
 typedef struct FKey FKey;
@@ -367,29 +368,36 @@ typedef struct WhereLevel WhereLevel;
 struct Db {
   char *zName;         /* Name of this database */
   Btree *pBt;          /* The B*Tree structure for this database file */
+  u8 inTrans;          /* 0: not writable.  1: Transaction.  2: Checkpoint */
+  u8 safety_level;     /* How aggressive at synching data to disk */
+  int cache_size;      /* Number of pages to use in the cache */
+  void *pAux;               /* Auxiliary data.  Usually NULL */
+  void (*xFreeAux)(void*);  /* Routine to free pAux */
+  DbSchema *pSchema;   /* Pointer to database schema (possibly shared) */
+};
+
+/*
+** An instance of the following structure stores a database schema.
+*/
+struct DbSchema {
   int schema_cookie;   /* Database schema version number for this file */
   Hash tblHash;        /* All tables indexed by name */
   Hash idxHash;        /* All (named) indices indexed by name */
   Hash trigHash;       /* All triggers indexed by name */
   Hash aFKey;          /* Foreign keys indexed by to-table */
-  u16 flags;           /* Flags associated with this database */
-  u8 inTrans;          /* 0: not writable.  1: Transaction.  2: Checkpoint */
-  u8 safety_level;     /* How aggressive at synching data to disk */
-  u8 file_format;      /* Schema format version for this file */
-  int cache_size;      /* Number of pages to use in the cache */
   Table *pSeqTab;      /* The sqlite_sequence table used by AUTOINCREMENT */
-  void *pAux;               /* Auxiliary data.  Usually NULL */
-  void (*xFreeAux)(void*);  /* Routine to free pAux */
+  u8 file_format;      /* Schema format version for this file */
+  u16 flags;           /* Flags associated with this schema */
 };
 
 /*
 ** These macros can be used to test, set, or clear bits in the 
 ** Db.flags field.
 */
-#define DbHasProperty(D,I,P)     (((D)->aDb[I].flags&(P))==(P))
-#define DbHasAnyProperty(D,I,P)  (((D)->aDb[I].flags&(P))!=0)
-#define DbSetProperty(D,I,P)     (D)->aDb[I].flags|=(P)
-#define DbClearProperty(D,I,P)   (D)->aDb[I].flags&=~(P)
+#define DbHasProperty(D,I,P)     (((D)->aDb[I].pSchema->flags&(P))==(P))
+#define DbHasAnyProperty(D,I,P)  (((D)->aDb[I].pSchema->flags&(P))!=0)
+#define DbSetProperty(D,I,P)     (D)->aDb[I].pSchema->flags|=(P)
+#define DbClearProperty(D,I,P)   (D)->aDb[I].pSchema->flags&=~(P)
 
 /*
 ** Allowed values for the DB.flags field.
@@ -518,6 +526,7 @@ struct sqlite3 {
 #define SQLITE_NoReadlock     0x00001000  /* Readlocks are omitted when 
                                           ** accessing read-only databases */
 #define SQLITE_IgnoreChecks   0x00002000  /* Do not enforce check constraints */
+#define SQLITE_ReadUncommitted 0x00004000  /* For shared-cache mode */
 
 /*
 ** Possible values for the sqlite.magic field.
@@ -672,7 +681,7 @@ struct Table {
   int tnum;        /* Root BTree node for this table (see note above) */
   Select *pSelect; /* NULL for tables.  Points to definition if a view. */
   u8 readOnly;     /* True if this table should not be written by the user */
-  u8 iDb;          /* Index into sqlite.aDb[] of the backend for this table */
+// u8 iDb;          /* Index into sqlite.aDb[] of the backend for this table */
   u8 isTransient;  /* True if automatically deleted when VDBE finishes */
   u8 hasPrimKey;   /* True if there exists a primary key */
   u8 keyConf;      /* What to do in case of uniqueness conflict on iPKey */
@@ -687,6 +696,7 @@ struct Table {
 #ifndef SQLITE_OMIT_ALTERTABLE
   int addColOffset;  /* Offset in CREATE TABLE statement to add a new column */
 #endif
+  DbSchema *pSchema;
 };
 
 /*
@@ -822,9 +832,10 @@ struct Index {
   int tnum;        /* Page containing root of this index in database file */
   u8 onError;      /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
   u8 autoIndex;    /* True if is automatically created (ex: by UNIQUE) */
-  u8 iDb;          /* Index in sqlite.aDb[] of where this index is stored */
+  // u8 iDb;          /* Index in sqlite.aDb[] of where this index is stored */
   char *zColAff;   /* String defining the affinity of each column */
   Index *pNext;    /* The next index associated with the same table */
+  DbSchema *pSchema;
   KeyInfo keyInfo; /* Info on how to order keys.  MUST BE LAST */
 };
 
@@ -934,7 +945,7 @@ struct AggInfo {
 struct Expr {
   u8 op;                 /* Operation performed by this node */
   char affinity;         /* The affinity of the column or 0 if not a column */
-  u8 iDb;                /* Database referenced by this expression */
+//u8 iDb;                /* Database referenced by this expression */
   u8 flags;              /* Various flags.  See below */
   CollSeq *pColl;        /* The collation type of the column or 0 */
   Expr *pLeft, *pRight;  /* Left and right subnodes */
@@ -950,6 +961,7 @@ struct Expr {
   Select *pSelect;       /* When the expression is a sub-select.  Also the
                          ** right side of "<expr> IN (<select>)" */
   Table *pTab;           /* Table for OP_Column expressions. */
+  DbSchema *pSchema;
 };
 
 /*
@@ -1277,7 +1289,7 @@ struct AuthContext {
 struct Trigger {
   char *name;             /* The name of the trigger                        */
   char *table;            /* The table or view to which the trigger applies */
-  u8 iDb;                 /* Database containing this trigger               */
+//u8 iDb;                 /* Database containing this trigger               */
   u8 iTabDb;              /* Database containing Trigger.table              */
   u8 op;                  /* One of TK_DELETE, TK_UPDATE, TK_INSERT         */
   u8 tr_tm;               /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
@@ -1286,7 +1298,7 @@ struct Trigger {
                              the <column-list> is stored here */
   int foreach;            /* One of TK_ROW or TK_STATEMENT */
   Token nameToken;        /* Token containing zName. Use during parsing only */
-
+  DbSchema *pSchema;
   TriggerStep *step_list; /* Link list of trigger program steps             */
   Trigger *pNext;         /* Next trigger associated with the table */
 };
@@ -1527,7 +1539,7 @@ void sqlite3SelectDelete(Select*);
 void sqlite3SelectUnbind(Select*);
 Table *sqlite3SrcListLookup(Parse*, SrcList*);
 int sqlite3IsReadOnly(Parse*, Table*, int);
-void sqlite3OpenTableForReading(Vdbe*, int iCur, Table*);
+void sqlite3OpenTableForReading(Vdbe*, int iCur, int iDb, Table*);
 void sqlite3OpenTable(Vdbe*, int iCur, Table*, int);
 void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
 void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
@@ -1700,6 +1712,9 @@ int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
 SqliteTsd *sqlite3Tsd();
 void sqlite3AttachFunctions(sqlite3 *);
 void sqlite3MinimumFileFormat(Parse*, int, int);
+void sqlite3SchemaFree(void *);
+DbSchema *sqlite3SchemaGet(Btree *);
+int sqlite3SchemaToIndex(sqlite3 *db, DbSchema *);
 
 void sqlite3MallocClearFailed();
 #ifdef NDEBUG
index afd88df5530d54e01360c052578f3ce941be055f..cd68f4497be63049c05c145bda83baf7acf02864 100644 (file)
@@ -58,6 +58,7 @@ void sqlite3BeginTrigger(
   int iDb;                /* The database to store the trigger in */
   Token *pName;           /* The unqualified db name */
   DbFixer sFix;
+  int iTabDb;
 
   if( isTemp ){
     /* If TEMP was specified, then the trigger name may not be qualified. */
@@ -82,7 +83,7 @@ void sqlite3BeginTrigger(
   */
   if( !pTableName || sqlite3Tsd()->mallocFailed ) goto trigger_cleanup;
   pTab = sqlite3SrcListLookup(pParse, pTableName);
-  if( pName2->n==0 && pTab && pTab->iDb==1 ){
+  if( pName2->n==0 && pTab && pTab->pSchema==db->aDb[1].pSchema ){
     iDb = 1;
   }
 
@@ -105,7 +106,7 @@ void sqlite3BeginTrigger(
   if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
     goto trigger_cleanup;
   }
-  if( sqlite3HashFind(&(db->aDb[iDb].trigHash), zName,pName->n+1) ){
+  if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash), zName,pName->n+1) ){
     sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
     goto trigger_cleanup;
   }
@@ -130,17 +131,18 @@ void sqlite3BeginTrigger(
         " trigger on table: %S", pTableName, 0);
     goto trigger_cleanup;
   }
+  iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
 
 #ifndef SQLITE_OMIT_AUTHORIZATION
   {
     int code = SQLITE_CREATE_TRIGGER;
-    const char *zDb = db->aDb[pTab->iDb].zName;
+    const char *zDb = db->aDb[iTabDb].zName;
     const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
-    if( pTab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
+    if( iTabDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
     if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){
       goto trigger_cleanup;
     }
-    if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(pTab->iDb),0,zDb)){
+    if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iTabDb),0,zDb)){
       goto trigger_cleanup;
     }
   }
@@ -161,8 +163,8 @@ void sqlite3BeginTrigger(
   pTrigger->name = zName;
   zName = 0;
   pTrigger->table = sqliteStrDup(pTableName->a[0].zName);
-  pTrigger->iDb = iDb;
-  pTrigger->iTabDb = pTab->iDb;
+  pTrigger->pSchema = db->aDb[iDb].pSchema;
+  pTrigger->iTabDb = iTabDb;
   pTrigger->op = op;
   pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
   pTrigger->pWhen = sqlite3ExprDup(pWhen);
@@ -196,16 +198,18 @@ void sqlite3FinishTrigger(
   Trigger *pTrig = 0;     /* The trigger whose construction is finishing up */
   sqlite3 *db = pParse->db;  /* The database */
   DbFixer sFix;
+  int iDb;                   /* Database containing the trigger */
 
   pTrig = pParse->pNewTrigger;
   pParse->pNewTrigger = 0;
   if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup;
+  iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
   pTrig->step_list = pStepList;
   while( pStepList ){
     pStepList->pTrig = pTrig;
     pStepList = pStepList->pNext;
   }
-  if( sqlite3FixInit(&sFix, pParse, pTrig->iDb, "trigger", &pTrig->nameToken) 
+  if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &pTrig->nameToken) 
           && sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){
     goto triggerfinish_cleanup;
   }
@@ -232,22 +236,22 @@ void sqlite3FinishTrigger(
     /* Make an entry in the sqlite_master table */
     v = sqlite3GetVdbe(pParse);
     if( v==0 ) goto triggerfinish_cleanup;
-    sqlite3BeginWriteOperation(pParse, 0, pTrig->iDb);
-    sqlite3OpenMasterTable(v, pTrig->iDb);
+    sqlite3BeginWriteOperation(pParse, 0, iDb);
+    sqlite3OpenMasterTable(v, iDb);
     addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
     sqlite3VdbeChangeP3(v, addr+2, pTrig->name, 0); 
     sqlite3VdbeChangeP3(v, addr+3, pTrig->table, 0); 
     sqlite3VdbeChangeP3(v, addr+6, (char*)pAll->z, pAll->n);
-    sqlite3ChangeCookie(db, v, pTrig->iDb);
+    sqlite3ChangeCookie(db, v, iDb);
     sqlite3VdbeAddOp(v, OP_Close, 0, 0);
-    sqlite3VdbeOp3(v, OP_ParseSchema, pTrig->iDb, 0, 
+    sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, 
        sqlite3MPrintf("type='trigger' AND name='%q'", pTrig->name), P3_DYNAMIC);
   }
 
   if( db->init.busy ){
     Table *pTab;
     Trigger *pDel;
-    pDel = sqlite3HashInsert(&db->aDb[pTrig->iDb].trigHash, 
+    pDel = sqlite3HashInsert(&db->aDb[iDb].pSchema->trigHash, 
                      pTrig->name, strlen(pTrig->name)+1, pTrig);
     if( pDel ){
       assert( sqlite3Tsd()->mallocFailed && pDel==pTrig );
@@ -445,7 +449,7 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName){
   for(i=OMIT_TEMPDB; i<db->nDb; i++){
     int j = (i<2) ? i^1 : i;  /* Search TEMP before MAIN */
     if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue;
-    pTrigger = sqlite3HashFind(&(db->aDb[j].trigHash), zName, nName+1);
+    pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName+1);
     if( pTrigger ) break;
   }
   if( !pTrigger ){
@@ -478,11 +482,11 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
   sqlite3 *db = pParse->db;
   int iDb;
 
-  iDb = pTrigger->iDb;
+  iDb = sqlite3SchemaToIndex(pParse->db, pTrigger->pSchema);
   assert( iDb>=0 && iDb<db->nDb );
   pTable = tableOfTrigger(db, pTrigger);
   assert(pTable);
-  assert( pTable->iDb==iDb || iDb==1 );
+  assert( pTable->pSchema==pTrigger->pSchema || iDb==1 );
 #ifndef SQLITE_OMIT_AUTHORIZATION
   {
     int code = SQLITE_DROP_TRIGGER;
@@ -528,7 +532,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
 void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
   Trigger *pTrigger;
   int nName = strlen(zName);
-  pTrigger = sqlite3HashInsert(&(db->aDb[iDb].trigHash), zName, nName+1, 0);
+  pTrigger = sqlite3HashInsert(&(db->aDb[iDb].pSchema->trigHash), zName, nName+1, 0);
   if( pTrigger ){
     Table *pTable = tableOfTrigger(db, pTrigger);
     assert( pTable!=0 );
@@ -620,7 +624,7 @@ static SrcList *targetSrcList(
   int iDb;             /* Index of the database to use */
   SrcList *pSrc;       /* SrcList to be returned */
 
-  iDb = pStep->pTrig->iDb;
+  iDb = sqlite3SchemaToIndex(pParse->db, pStep->pTrig->pSchema);
   if( iDb==0 || iDb>=2 ){
     assert( iDb<pParse->db->nDb );
     sDb.z = (u8*)pParse->db->aDb[iDb].zName;
index 5b5c2e9544eb5cbe517a6da52b366a29c3f18d3a..fd6edc60fbcb27a080dc840e6dd897862a65399e 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.114 2005/12/06 12:53:01 danielk1977 Exp $
+** $Id: update.c,v 1.115 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -89,6 +89,7 @@ void sqlite3Update(
   int openAll = 0;       /* True if all indices need to be opened */
   AuthContext sContext;  /* The authorization context */
   NameContext sNC;       /* The name-context to resolve expressions in */
+  int iDb;               /* Database containing the table being updated */
 
 #ifndef SQLITE_OMIT_TRIGGER
   int isView;                  /* Trying to update a view */
@@ -107,6 +108,7 @@ void sqlite3Update(
   */
   pTab = sqlite3SrcListLookup(pParse, pTabList);
   if( pTab==0 ) goto update_cleanup;
+  iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
 
   /* Figure out if we have any triggers and if the table being
   ** updated is a view
@@ -192,7 +194,7 @@ void sqlite3Update(
     {
       int rc;
       rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName,
-                           pTab->aCol[j].zName, db->aDb[pTab->iDb].zName);
+                           pTab->aCol[j].zName, db->aDb[iDb].zName);
       if( rc==SQLITE_DENY ){
         goto update_cleanup;
       }else if( rc==SQLITE_IGNORE ){
@@ -257,7 +259,7 @@ void sqlite3Update(
   v = sqlite3GetVdbe(pParse);
   if( v==0 ) goto update_cleanup;
   if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
-  sqlite3BeginWriteOperation(pParse, 1, pTab->iDb);
+  sqlite3BeginWriteOperation(pParse, 1, iDb);
 
   /* If we are trying to update a view, realize that view into
   ** a ephemeral table.
@@ -307,7 +309,7 @@ void sqlite3Update(
       /* Open a cursor and make it point to the record that is
       ** being updated.
       */
-      sqlite3OpenTableForReading(v, iCur, pTab);
+      sqlite3OpenTableForReading(v, iCur, iDb, pTab);
     }
     sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
 
@@ -362,7 +364,7 @@ void sqlite3Update(
     ** action, then we need to open all indices because we might need
     ** to be deleting some records.
     */
-    sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+    sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
     sqlite3VdbeAddOp(v, OP_OpenWrite, iCur, pTab->tnum);
     sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
     if( onError==OE_Replace ){
@@ -378,7 +380,7 @@ void sqlite3Update(
     }
     for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
       if( openAll || aIdxUsed[i] ){
-        sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+        sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
         sqlite3VdbeOp3(v, OP_OpenWrite, iCur+i+1, pIdx->tnum,
                        (char*)&pIdx->keyInfo, P3_KEYINFO);
         assert( pParse->nTab>iCur+i+1 );
index 4dff6b21f2c85b641be539ecb605675893dc4715..a42964d1714cf02cace66a6b0d02f18abaafc6d1 100644 (file)
@@ -43,7 +43,7 @@
 ** in this file for details.  If in doubt, do not deviate from existing
 ** commenting and indentation practices when changing or adding code.
 **
-** $Id: vdbe.c,v 1.510 2006/01/03 15:16:26 drh Exp $
+** $Id: vdbe.c,v 1.511 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -2422,11 +2422,11 @@ case OP_SetCookie: {       /* no-push */
   rc = sqlite3BtreeUpdateMeta(pDb->pBt, 1+pOp->p2, (int)pTos->i);
   if( pOp->p2==0 ){
     /* When the schema cookie changes, record the new cookie internally */
-    pDb->schema_cookie = pTos->i;
+    pDb->pSchema->schema_cookie = pTos->i;
     db->flags |= SQLITE_InternChanges;
   }else if( pOp->p2==1 ){
     /* Record changes in the file format */
-    pDb->file_format = pTos->i;
+    pDb->pSchema->file_format = pTos->i;
   }
   assert( (pTos->flags & MEM_Dyn)==0 );
   pTos--;
@@ -2530,8 +2530,8 @@ case OP_OpenWrite: {       /* no-push */
   assert( pX!=0 );
   if( pOp->opcode==OP_OpenWrite ){
     wrFlag = 1;
-    if( pDb->file_format < p->minWriteFileFormat ){
-      p->minWriteFileFormat = pDb->file_format;
+    if( pDb->pSchema->file_format < p->minWriteFileFormat ){
+      p->minWriteFileFormat = pDb->pSchema->file_format;
     }
   }else{
     wrFlag = 0;
index e3dc7aca6a98e60d081ef36b6111ca91f03edcda..86d3ac3bc99c19be18afdbe7eb99242dc74ff1b0 100644 (file)
@@ -16,7 +16,7 @@
 ** so is applicable.  Because this module is responsible for selecting
 ** indices, you might also think of this module as the "query optimizer".
 **
-** $Id: where.c,v 1.189 2005/12/21 18:36:46 drh Exp $
+** $Id: where.c,v 1.190 2006/01/05 11:34:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -1554,8 +1554,9 @@ WhereInfo *sqlite3WhereBegin(
   sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
   pLevel = pWInfo->a;
   for(i=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){
-    Table *pTab;
-    Index *pIx;
+    Table *pTab;     /* Table to open */
+    Index *pIx;      /* Index used to access pTab (if any) */
+    int iDb;         /* Index of database containing table/index */
     int iIdxCur = pLevel->iIdxCur;
 
 #ifndef SQLITE_OMIT_EXPLAIN
@@ -1576,13 +1577,15 @@ WhereInfo *sqlite3WhereBegin(
 #endif /* SQLITE_OMIT_EXPLAIN */
     pTabItem = &pTabList->a[pLevel->iFrom];
     pTab = pTabItem->pTab;
+    iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
     if( pTab->isTransient || pTab->pSelect ) continue;
     if( (pLevel->flags & WHERE_IDX_ONLY)==0 ){
-      sqlite3OpenTableForReading(v, pTabItem->iCursor, pTab);
+      sqlite3OpenTableForReading(v, pTabItem->iCursor, iDb, pTab);
     }
     pLevel->iTabCur = pTabItem->iCursor;
     if( (pIx = pLevel->pIdx)!=0 ){
-      sqlite3VdbeAddOp(v, OP_Integer, pIx->iDb, 0);
+      assert( pIx->pSchema==pTab->pSchema );
+      sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
       VdbeComment((v, "# %s", pIx->zName));
       sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIx->tnum,
                      (char*)&pIx->keyInfo, P3_KEYINFO);
@@ -1590,7 +1593,7 @@ WhereInfo *sqlite3WhereBegin(
     if( (pLevel->flags & WHERE_IDX_ONLY)!=0 ){
       sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, pIx->nColumn+1);
     }
-    sqlite3CodeVerifySchema(pParse, pTab->iDb);
+    sqlite3CodeVerifySchema(pParse, iDb);
   }
   pWInfo->iTop = sqlite3VdbeCurrentAddr(v);
 
index 95e16a6d04bca1155bfcae169cd6bfe9deb57e75..2bf98702b9014b37abfd94bc5d6361df410a1715 100644 (file)
@@ -10,7 +10,7 @@
 #***********************************************************************
 # This file runs all tests.
 #
-# $Id: all.test,v 1.31 2005/12/15 10:11:32 danielk1977 Exp $
+# $Id: all.test,v 1.32 2006/01/05 11:34:34 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -56,6 +56,11 @@ set EXCLUDE {
   malloc.test
   misuse.test
   memleak.test
+
+  malloc2.test
+  malloc3.test
+  malloc4.test
+  malloc5.test
 }
 
 # Test files btree2.test and btree4.test don't work if the 
@@ -126,7 +131,7 @@ if {$::tcl_platform(platform)=="unix"} {
 #
 catch {source $testdir/misuse.test}
 set sqlite_open_file_count 0
-catch {source $testdir/malloc.test}
+catch {source $testdir/malloc.test}
 
 catch {db close}
 set sqlite_open_file_count 0
index 52ff429b58b6df75e9f07673115656691611d26a..f0e5d86a97f83e3ded67329c8ae84bee36ecc170 100644 (file)
@@ -29,7 +29,7 @@
 # The solution to the problem was to detect that the table is locked
 # before the index entry is deleted.
 #
-# $Id: delete2.test,v 1.5 2006/01/03 00:33:50 drh Exp $
+# $Id: delete2.test,v 1.6 2006/01/05 11:34:34 danielk1977 Exp $
 #
 
 set testdir [file dirname $argv0]
@@ -67,6 +67,7 @@ integrity_check delete2-1.5
 
 # Try to delete a row from the table. The delete should fail.
 #
+breakpoint
 do_test delete2-1.6 {
   catchsql {
     DELETE FROM q WHERE rowid=1
index ddbe3415c599a89bce2bec5a9d6c49956374ae17..0abadc3ef80bcbb1a8267691199c9ffb85b63d4b 100644 (file)
@@ -11,9 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the SELECT statement.
 #
-# $Id: shared.test,v 1.1 2005/12/30 16:28:02 danielk1977 Exp $
-
-set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+# $Id: shared.test,v 1.2 2006/01/05 11:34:34 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -23,6 +21,7 @@ ifcapable !shared_cache {
   finish_test
   return
 }
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
 
 # Test organization:
 #
@@ -31,6 +30,7 @@ ifcapable !shared_cache {
 # shared-2.*: Test that a read transaction can co-exist with a 
 #             write-transaction, including a simple test to ensure the 
 #             external locking protocol is still working.
+# shared-3.*: Simple test of read-uncommitted mode.
 #
 
 do_test shared-1.1 {
@@ -73,62 +73,37 @@ do_test shared-1.4 {
   } db2
 } {1 {database is locked}}
 do_test shared-1.5 {
-  # Using connection 2 (the one without the open transaction), create a 
-  # new table and add a row to it. This is permitted as the transaction
-  # started by connection 1 is currently a read transaction.
-  execsql {
+  # Using connection 2 (the one without the open transaction), try to create
+  # a new table. This should fail because of the open read transaction 
+  # held by connection 1.
+  catchsql {
     CREATE TABLE def(d, e, f);
-    INSERT INTO def VALUES('I', 'II', 'III');
   } db2
-} {}
+} {1 {database is locked}}
 do_test shared-1.6 {
-  # Upgrade connection 1's transaction to a write transaction. Insert
-  # a row into table def - the table just created by connection 2.
-  #
-  # Connection 1 is able to see table def, even though it was created 
-  # "after" the connection 1 transaction was started. This is because no
-  # lock was established on the sqlite_master table.
-
-# Todo: Remove this. Because the implementation does not include
-# shared-schemas yet, we need to run some query (that will fail at 
-# OP_VerifyCookie) so that connection 1 picks up the schema change
-# made via connection 2. Otherwise the sqlite3_prepare("INSERT INTO def...")
-# below will fail.
-execsql {
-  SELECT * FROM sqlite_master;
-}
-
+  # Upgrade connection 1's transaction to a write transaction. Create
+  # a new table - def - and insert a row into it. Because the connection 1
+  # transaction modifies the schema, it should not be possible for 
+  # connection 2 to access the database at all until the connection 1 
+  # has finished the transaction.
   execsql {
+    CREATE TABLE def(d, e, f);
     INSERT INTO def VALUES('IV', 'V', 'VI');
   }
 } {}
 do_test shared-1.7 {
   # Read from the sqlite_master table with connection 1 (inside the 
-  # transaction). Then test that we can no longer create a table 
-  # with connection 2. This is because of the read-lock on sqlite_master.
+  # transaction). Then test that we can not do this with connection 2. This
+  # is because of the schema-modified lock established by connection 1 
+  # in the previous test case.
   execsql {
     SELECT * FROM sqlite_master;
   }
-  catchsql {
-    CREATE TABLE ghi(g, h, i);
-  } db2
-} {1 {database is locked}}
-do_test shared-1.8 {
-  # Check that connection 2 can read the sqlite_master table. Then
-  # create a table using connection 1 (this should write-lock the 
-  # sqlite_master table). Then try to read sqlite_master again using 
-  # connection 2 and verify that the write-lock prevents this.
-  execsql {
-    SELECT * FROM sqlite_master;
-  } db2
-  execsql {
-    CREATE TABLE ghi(g, h, i);
-  } 
   catchsql {
     SELECT * FROM sqlite_master;
   } db2
 } {1 {database is locked}}
-do_test shared-1.9 {
+do_test shared-1.8 {
   # Commit the connection 1 transaction.
   execsql {
     COMMIT;
@@ -180,7 +155,7 @@ do_test shared-2.3 {
   ] [
     catchsql { SELECT * FROM def; } db2
   ]
-} {0 {I II III IV V VI} 1 {database is locked}}
+} {0 {IV V VI} 1 {database is locked}}
 do_test shared-2.4 {
   # Commit the open transaction on db. db2 still holds a read-transaction.
   # This should prevent db3 from writing to the database, but not from 
@@ -193,8 +168,51 @@ do_test shared-2.4 {
   ] [
     catchsql { INSERT INTO def VALUES('X', 'XI', 'XII'); } db3
   ]
-} {0 {I II III IV V VI VII VIII IX} 1 {database is locked}}
+} {0 {IV V VI VII VIII IX} 1 {database is locked}}
+
+catchsql COMMIT db2
 
+do_test shared-3.1.1 {
+  # This test case starts a linear scan of table 'seq' using a 
+  # read-uncommitted connection. In the middle of the scan, rows are added
+  # to the end of the seq table (ahead of the current cursor position).
+  # The uncommitted rows should be included in the results of the scan.
+  execsql "
+    CREATE TABLE seq(i, x);
+    INSERT INTO seq VALUES(1, '[string repeat X 500]');
+    INSERT INTO seq VALUES(2, '[string repeat X 500]');
+  "
+  execsql {SELECT * FROM sqlite_master} db2
+  execsql {PRAGMA read_uncommitted = 1} db2
+
+  set ret [list]
+  db2 eval {SELECT i FROM seq} {
+    if {$i < 4} {
+      execsql {
+        INSERT INTO seq SELECT i + (SELECT max(i) FROM seq), x FROM seq;
+      }
+    }
+    lappend ret $i
+  }
+  set ret
+} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}
+do_test shared-3.1.2 {
+  # Another linear scan through table seq using a read-uncommitted connection.
+  # This time, delete each row as it is read. Should not affect the results of
+  # the scan, but the table should be empty after the scan is concluded 
+  # (test 3.1.3 verifies this).
+  set ret [list]
+  db2 eval {SELECT i FROM seq} {
+    db eval {DELETE FROM seq WHERE i = $i}
+    lappend ret $i
+  }
+  set ret
+} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}
+do_test shared-3.1.3 {
+  execsql {
+    SELECT * FROM seq;
+  }
+} {}
 
 catch {db close}
 catch {db2 close}
index 3d011b918e729b48f5b8377e4deb1e7a83c43dfc..57d9803c36c73b861a27fc25551fd929ad15c0d1 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements some common TCL routines used for regression
 # testing the SQLite library
 #
-# $Id: tester.tcl,v 1.56 2006/01/03 00:33:50 drh Exp $
+# $Id: tester.tcl,v 1.57 2006/01/05 11:34:34 danielk1977 Exp $
 
 # Make sure tclsqlite3 was compiled correctly.  Abort now with an
 # error message if not.
@@ -449,7 +449,7 @@ proc check_for_leaks {} {
 
     # The first command in this block will probably fail on windows. This
     # means there will be no stack dump available.
-    if {$cnt < 25} {
+    if {$cnt < 25 && $backtrace!=""} {
       catch {
         set stuff [eval "exec addr2line -e ./testfixture -f $backtrace"]
         foreach {func line} $stuff {
diff --git a/www/sharedcache.tcl b/www/sharedcache.tcl
new file mode 100644 (file)
index 0000000..a1f95ac
--- /dev/null
@@ -0,0 +1,223 @@
+#
+# Run this script to generated a sharedcache.html output file
+#
+set rcsid {$Id: }
+source common.tcl
+header {SQLite Shared-Cache Mode}
+
+proc HEADING {level title} {
+  global pnum
+  incr pnum($level)
+  foreach i [array names pnum] {
+    if {$i>$level} {set pnum($i) 0}
+  }
+  set h [expr {$level+1}]
+  if {$h>6} {set h 6}
+  set n $pnum(1).$pnum(2)
+  for {set i 3} {$i<=$level} {incr i} {
+    append n .$pnum($i)
+  }
+  puts "<h$h>$n $title</h$h>"
+}
+set pnum(1) 0
+set pnum(2) 0
+set pnum(3) 0
+set pnum(4) 0
+set pnum(5) 0
+set pnum(6) 0
+set pnum(7) 0
+set pnum(8) 0
+
+HEADING 1 {SQLite Shared-Cache Mode}
+
+puts {
+<p>Starting with version 3.3.0, SQLite includes a special "shared-cache"
+mode (disabled by default) intended for use in embedded servers. If
+shared-cache mode is enabled and a thread establishes multiple connections
+to the same database, the connections share a single data and schema cache.
+This can significantly reduce the quantity of memory and IO required by
+the system.</p>
+
+<p>Using shared-cache mode imposes some extra restrictions on 
+passing database handles between threads and also changes the semantics
+of the locking model in some cases. These details are described in full by
+this document. A basic understanding of the normal SQLite locking model (see
+<a href="lockingv3.html">File Locking And Concurrency In SQLite Version 3</a>
+for details) is assumed.
+</p>
+}
+
+HEADING 1 {Shared-Cache Locking Model}
+
+puts {
+<p>Externally, from the point of view of another process or thread, two
+or more database connections using a shared-cache appear as a single 
+connection. The locking protocol used to arbitrate between multiple 
+shared-caches or regular database users is described elsewhere.
+</p>
+
+<table style="margin:auto">
+<tr><td>
+<pre>
+            +--------------+      +--------------+
+            | Connection 2 |      | Connection 3 |
+            +--------------+      +--------------+
+                         |          |
+                         V          V
++--------------+       +--------------+
+| Connection 1 |       | Shared cache |
++--------------+       +--------------+
+            |            |
+            V            V
+          +----------------+
+          |    Database    |
+          +----------------+
+</pre>
+</table>
+<p style="font-style:italic;text-align:center">Figure 1</p>
+
+<p>Figure 1 depicts an example runtime configuration where three 
+database connections have been established. Connection 1 is a normal
+SQLite database connection. Connections 2 and 3 share a cache (and so must
+have been established by the same process thread). The normal locking
+protocol is used to serialize database access between connection 1 and
+the shared cache. The internal protocol used to serialize (or not, see
+"Read-Uncommitted Isolation Mode" below) access to the shared-cache by
+connections 2 and 3 is described in the remainder of this section.
+</p>
+
+<p>There are three levels to the shared-cache locking model, 
+transaction level locking, table level locking and schema level locking. 
+They are described in the following three sub-sections.</p>
+
+}
+
+HEADING 2 {Transaction Level Locking}
+
+puts {
+<p>SQLite connections can open two kinds of transactions, read and write
+transactions. This is not done explicitly, a transaction is implicitly a
+read-transaction until it first writes to a database table, at which point
+it becomes a write-transaction.
+</p>
+<p>At most one connection to a single shared cache may open a 
+write transaction at any one time. This may co-exist with any number of read 
+transactions. 
+</p>
+}
+
+HEADING 2 {Table Level Locking}
+
+puts {
+<p>When two or more connections use a shared-cache, locks are used to 
+serialize concurrent access attempts on a per-table basis. Tables support 
+two types of locks, "read-locks" and "write-locks". Locks are granted to
+connections - at any one time, each database connection has either a
+read-lock, write-lock or no lock on each database table.
+</p>
+
+<p>At any one time, a single table may have any number of active read-locks
+or a single active write lock. To read data a table, a connection must 
+first obtain a read-lock. To write to a table, a connection must obtain a 
+write-lock on that table. If a required table lock cannot be obtained,
+the query fails and SQLITE_BUSY is returned to the caller.
+</p> 
+
+<p><b>TODO: Should we be invoking the busy-handler here? Just waiting won't do
+any good, but something else might...  </b></p>
+
+<p>Once a connection obtains a table lock, it is not released until the
+current transaction (read or write) is concluded.
+</p>
+}
+
+HEADING 3 {Read-Uncommitted Isolation Mode}
+
+puts {
+<p>The behaviour described above may be modified slightly by using the 
+<i>read_uncommitted</i> pragma to change the isolation level from serialized 
+(the default), to read-uncommitted.</p>
+
+<p> A database connection in read-uncommitted mode does not attempt 
+to obtain read-locks before reading from database tables as described 
+above. This can lead to inconsistent query results if another database
+connection modifies a table while it is being read, but it also means that
+a read-transaction opened by a connection in read-uncommitted mode can
+neither block nor be blocked by any other connection.</p>
+
+<p>Read-uncommitted mode has no effect on the locks required to write to
+database tables (i.e. read-uncommitted connections must still obtain 
+write-locks and hence database writes may still block or be blocked). 
+Also, read-uncommitted mode has no effect on the <i>sqlite_master</i> 
+locks required by the rules enumerated below (see section 
+"Schema (sqlite_master) Level Locking").
+</p>
+
+<pre>
+  /* Set the value of the read-uncommitted flag:
+  **
+  **   True  -> Set the connection to read-uncommitted mode.
+  **   False -> Set the connectino to serialized (the default) mode.
+  */
+  PRAGMA read_uncommitted = &lt;boolean&gt;;
+
+  /* Retrieve the current value of the read-uncommitted flag */
+  PRAGMA read_uncommitted;
+</pre>
+}
+
+HEADING 2 {Schema (sqlite_master) Level Locking}
+
+puts {
+<p>The <i>sqlite_master</i> table supports shared-cache read and write 
+locks in the same way as all other database tables (see description 
+above). The following special rules also apply:
+</p>
+
+<ul>
+<li>A connection must obtain a read-lock on <i>sqlite_master</i> before 
+accessing any database tables or obtaining any other read or write locks.</li>
+<li>Before executing a statement that modifies the database schema (i.e. 
+a CREATE or DROP TABLE statement), a connection must obtain a write-lock on 
+<i>sqlite_master</i>.
+</li>
+<li>A connection may not compile an SQL statement that refers to database
+tables if any other connection is holding a write-lock on <i>sqlite_master</i>.
+</li>
+</ul>
+}
+
+HEADING 1 {Thread Related Issues}
+
+puts {
+<p>When shared-cache mode is enabled, a database connection may only be
+used by the thread that called sqlite3_open() to create it. If another 
+thread attempts to use the database connection, in most cases an 
+SQLITE_MISUSE error is returned. However this is not guaranteed and 
+programs should not depend on this behaviour, in some cases a segfault 
+may result.
+</p>
+}
+
+HEADING 1 {Enabling Shared-Cache Mode}
+
+puts {
+<p>Shared-cache mode is enabled on a thread-wide basis. Using the C 
+interface, the following API can be used to enable or disable shared-cache
+mode for the calling thread:
+</p>
+
+<pre>
+int sqlite3_enable_shared_cache(int);
+</pre>
+
+<p>It is illegal to call sqlite3_enable_shared_cache() if one or more 
+open database connections were opened by the calling thread. If the argument
+is non-zero, shared-cache mode is enabled. If the argument is zero,
+shared-cache mode is disabled. The return value is either SQLITE_OK (if the
+operation was successful), SQLITE_NOMEM (if a malloc() failed), or
+SQLITE_MISUSE (if the thread has open database connections).
+</p>
+}
+
+footer $rcsid