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
optoverview.html \
pragma.html \
quickstart.html \
+ sharedcache.html \
speed.html \
sqlite.html \
support.html \
-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
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
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
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
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
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
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
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
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
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
-cb9095ac52e76926f274678ef55ebb9df4b9fcac
\ No newline at end of file
+deeda0dc06c1595aedd8d06a0c4e88a8abf78cf7
\ No newline at end of file
** 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>
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{
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
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. */
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];
int i;
int nAlloc;
-
/* Look up the table being altered. */
assert( pParse->pNewTable==0 );
if( sqlite3Tsd()->mallocFailed ) goto exit_begin_add_column;
}
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.
pCol->zType = 0;
pCol->pDflt = 0;
}
- pNew->iDb = iDb;
+ pNew->pSchema = pParse->db->aDb[iDb].pSchema;
pNew->addColOffset = pTab->addColOffset;
pNew->nRef = 1;
*************************************************************************
** 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"
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 ){
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
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);
*/
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;
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);
}
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);
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);
}
*************************************************************************
** 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"
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
{
** 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"
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;
}
}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{
** 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
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*);
};
/*
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
** 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
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;
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 ){
*/
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 ){
#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
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
pBt->pNext = pTsd->pBtree;
pTsd->pBtree = pBt;
}
- pBt->nRef = 1;
#endif
+ pBt->nRef = 1;
*ppBtree = p;
return SQLITE_OK;
}
** 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);
/* 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;
}
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"
);
}
}
return rc;
}
}
- pCur = sqliteMallocRaw( sizeof(*pCur) );
+ pCur = sqliteMalloc( sizeof(*pCur) );
if( pCur==0 ){
rc = SQLITE_NOMEM;
goto create_cursor_exception;
}
pCur->pPrev = 0;
pBt->pCursor = pCur;
- pCur->isValid = 0;
+ pCur->eState = CURSOR_INVALID;
*ppCur = pCur;
return SQLITE_OK;
*/
int sqlite3BtreeCloseCursor(BtCursor *pCur){
BtShared *pBt = pCur->pBtree->pBt;
+ restoreCursorPosition(pCur, 0);
if( pCur->pPrev ){
pCur->pPrev->pNext = pCur->pNext;
}else{
** 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;
}
/*
** 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;
}
/*
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);
** 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;
}
/*
** 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;
}
/*
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 );
** 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;
}
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);
MemPage *pPage;
int idxParent;
- assert( pCur->isValid );
+ assert( pCur->eState==CURSOR_VALID );
pPage = pCur->pPage;
assert( pPage!=0 );
assert( !isRootPage(pPage) );
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);
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;
}
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));
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;
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;
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;
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;
** 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);
}
/*
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;
}
do{
if( isRootPage(pPage) ){
*pRes = 1;
- pCur->isValid = 0;
+ pCur->eState = CURSOR_INVALID;
return SQLITE_OK;
}
moveToParent(pCur);
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;
}
}else{
while( pCur->idx==0 ){
if( isRootPage(pPage) ){
- pCur->isValid = 0;
+ pCur->eState = CURSOR_INVALID;
*pRes = 1;
return SQLITE_OK;
}
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);
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;
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);
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
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;
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;
}
/*
** 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;
}
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);
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.
** 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_
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 *);
** 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>
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;
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;
*/
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);
}
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;
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 );
*/
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);
}
*/
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);
}
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 ){
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;
*/
#ifndef SQLITE_OMIT_AUTOINCREMENT
if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){
- db->aDb[iDb].pSeqTab = pTable;
+ pTable->pSchema->pSeqTab = pTable;
}
#endif
** 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);
}
** 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;
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++] = '(';
){
Table *p;
sqlite3 *db = pParse->db;
+ int iDb;
if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite3Tsd()->mallocFailed ) {
return;
assert( !db->init.busy || !pSelect );
+ iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
+
#ifndef SQLITE_OMIT_CHECK
/* Resolve names in all CHECK constraint expressions.
*/
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);
/* 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);
"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
#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);
}
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;
#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;
Token sEnd;
DbFixer sFix;
Token *pName;
+ int iDb;
if( pParse->nVar>0 ){
sqlite3ErrorMsg(pParse, "parameters are not allowed in views");
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);
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++;
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);
#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;
}
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
}
}
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;
}
}
}
#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;
}
v = sqlite3GetVdbe(pParse);
if( v ){
Trigger *pTrigger;
- int iDb = pTab->iDb;
Db *pDb = &db->aDb[iDb];
sqlite3BeginWriteOperation(pParse, 0, iDb);
*/
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;
}
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
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 ){
** 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
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];
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 */
*/
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 */
Index *pIndex;
Vdbe *v;
sqlite3 *db = pParse->db;
+ int iDb;
if( pParse->nErr || sqlite3Tsd()->mallocFailed ){
goto exit_drop_index;
"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;
}
/* 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),
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 ){
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);
}
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);
}
}
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);
}
** 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"
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);
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 */
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;
}
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.
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);
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);
}
}
}
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);
** 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>
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;
}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++){
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;
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 ){
** 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"
**
** 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;
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 */
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;
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 ){
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);
** 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;
}
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);
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);
** 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"
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
*/
#endif
db->magic = SQLITE_MAGIC_ERROR;
+ sqliteFree(db->aDb[1].pSchema);
sqliteFree(db);
sqlite3MallocAllow();
return SQLITE_OK;
#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);
sqlite3 **ppDb /* OUT: Returned database handle */
){
sqlite3 *db;
- int rc, i;
+ int rc;
CollSeq *pColl;
assert( !sqlite3Tsd()->mallocFailed );
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
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.
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.
*************************************************************************
** 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"
/* 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;
/* 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;
/* 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);
/* 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;
** 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"
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 ){
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.
}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.
** 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;
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);
** 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"
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.
** 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);
*/
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 ){
*************************************************************************
** 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_
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;
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.
#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.
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 */
#ifndef SQLITE_OMIT_ALTERTABLE
int addColOffset; /* Offset in CREATE TABLE statement to add a new column */
#endif
+ DbSchema *pSchema;
};
/*
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 */
};
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 */
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;
};
/*
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 */
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 */
};
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);
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
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. */
*/
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;
}
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;
}
" 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;
}
}
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);
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;
}
/* 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 );
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 ){
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;
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 );
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;
** 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"
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 */
*/
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
{
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 ){
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.
/* 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);
** 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 ){
}
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 );
** 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"
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--;
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;
** 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"
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
#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);
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);
#***********************************************************************
# 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
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
#
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
# 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]
# 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
# 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
finish_test
return
}
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
# Test organization:
#
# 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 {
} 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;
] [
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
] [
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}
# 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.
# 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 {
--- /dev/null
+#
+# 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 = <boolean>;
+
+ /* 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