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