1
0
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:
danielk1977
2006-01-05 11:34:32 +00:00
parent a6370df1e1
commit da18423620
27 changed files with 1077 additions and 375 deletions

View File

@@ -455,6 +455,9 @@ pragma.html: $(TOP)/www/pragma.tcl
lockingv3.html: $(TOP)/www/lockingv3.tcl lockingv3.html: $(TOP)/www/lockingv3.tcl
tclsh $(TOP)/www/lockingv3.tcl >lockingv3.html 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 mingw.html: $(TOP)/www/mingw.tcl
tclsh $(TOP)/www/mingw.tcl >mingw.html tclsh $(TOP)/www/mingw.tcl >mingw.html
@@ -535,6 +538,7 @@ DOC = \
optoverview.html \ optoverview.html \
pragma.html \ pragma.html \
quickstart.html \ quickstart.html \
sharedcache.html \
speed.html \ speed.html \
sqlite.html \ sqlite.html \
support.html \ support.html \

View File

@@ -1,5 +1,5 @@
C Bug\sfix\sin\sthe\sIF\sNOT\sEXISTS\slogic.\s(CVS\s2858) C Add\sthe\sshared\sschema/pager\smodifications.\sVery\sfew\stests\sso\sfar.\s(CVS\s2859)
D 2006-01-04T21:40:07 D 2006-01-05T11:34:33
F Makefile.in e3c6b3a38d734d41574c04f2fc90d18de2b87102 F Makefile.in e3c6b3a38d734d41574c04f2fc90d18de2b87102
F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092 F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -19,7 +19,7 @@ F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538
F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
F ltmain.sh f6b283068efa69f06eb8aa1fe4bddfdbdeb35826 F ltmain.sh f6b283068efa69f06eb8aa1fe4bddfdbdeb35826
F main.mk c5c5087f957210233fd6ecb5e7c161c2c90038b9 F main.mk a0d69de7dcb8f2d439412a78a58a999edc28561c
F mkdll.sh 5ec23622515d5bf8969404e80cfb5e220ddf0512 F mkdll.sh 5ec23622515d5bf8969404e80cfb5e220ddf0512
F mkopcodec.awk bd46ad001c98dfbab07b1713cb8e692fa0e5415d F mkopcodec.awk bd46ad001c98dfbab07b1713cb8e692fa0e5415d
F mkopcodeh.awk 071dbba4eaf56c8d643baf4604a043af35683316 F mkopcodeh.awk 071dbba4eaf56c8d643baf4604a043af35683316
@@ -30,25 +30,25 @@ F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.def c413e514217736884254739a105c8c942fdf0c2f F sqlite3.def c413e514217736884254739a105c8c942fdf0c2f
F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
F src/alter.c 5905a3372379daa4f860199452b4de5a836e53f3 F src/alter.c e9deb3f4fd7c663a0d1f235d541bc5ea1f2cfa8b
F src/analyze.c ea42005eed52c382fcc7ef66969e7f1858597633 F src/analyze.c d821684cdb4d0403e327e4a3440a832e9e54fa3a
F src/attach.c 07822dbd2dcf6de548aba6cb24142aec800fa3b6 F src/attach.c 999104c56a60b88eab11ef9c8f40dedf1650b287
F src/auth.c 31e2304bef67f44d635655f44234387ea7d21454 F src/auth.c cdec356a5cd8b217c346f816c5912221537fe87f
F src/btree.c ec89192ceeae9343c5d8f2564b8c9b8c958e6bfc F src/btree.c f848dd6e590f6bb93e2f229d87080c900d49bd4c
F src/btree.h d6481f9253f0b5fa40b35da4b93a54d0f9c5f9f2 F src/btree.h 96b8c00c6e11ff92f8d3d6a7a0ff358bd10d8f19
F src/build.c 7bf6f9ba5b2ea7c654986085211fcd9794cfb5c8 F src/build.c 6b14101f1ed5328c815e12baec11dcec97eed096
F src/callback.c 62066afd516f220575e81b1a1239ab92a2eae252 F src/callback.c 62066afd516f220575e81b1a1239ab92a2eae252
F src/complete.c df1681cef40dec33a286006981845f87b194e7a4 F src/complete.c df1681cef40dec33a286006981845f87b194e7a4
F src/date.c bb079317bff6a2b78aba5c0d2ddae5f6f03acfb7 F src/date.c bb079317bff6a2b78aba5c0d2ddae5f6f03acfb7
F src/delete.c ebb83753b6eca870c90edf2de736ab547685c326 F src/delete.c 2479a8419c76cd4c13f2181d399425ec215ceeb9
F src/experimental.c 50c1e3b34f752f4ac10c36f287db095c2b61766d F src/experimental.c 50c1e3b34f752f4ac10c36f287db095c2b61766d
F src/expr.c 714a06707facc5152228f2c1758d5003bd3a5b3c F src/expr.c ed2a272c7afd63232ca04881159ce2366266e35d
F src/func.c 25f1e5710b71cb345b492a18088f546188599f6b F src/func.c 25f1e5710b71cb345b492a18088f546188599f6b
F src/hash.c 8747cf51d12de46512880dfcf1b68b4e24072863 F src/hash.c 8747cf51d12de46512880dfcf1b68b4e24072863
F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
F src/insert.c a5629e462560d6aa9be7e0dafb6ed1518bb623bb F src/insert.c d167f9d41932ddaff9162f116e2abc514b0680b6
F src/legacy.c 59757d857ab95fcbb0ac27692d3201e35f093dd7 F src/legacy.c 59757d857ab95fcbb0ac27692d3201e35f093dd7
F src/main.c c93f80d1fcaf3ed508d62a163819b10a730c2fb7 F src/main.c 3a9689e4127ad7d4d417ff4e0bed2dd2b76ea445
F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217 F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217
F src/os.c 7b4a002d9c9421580276db55d2329636a604e8ef F src/os.c 7b4a002d9c9421580276db55d2329636a604e8ef
F src/os.h cc99e1515696728ba64c77fffa781ebadea34619 F src/os.h cc99e1515696728ba64c77fffa781ebadea34619
@@ -62,14 +62,14 @@ F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
F src/pager.c 681b4e39d581ead8fd54283176138bec924a4bae F src/pager.c 681b4e39d581ead8fd54283176138bec924a4bae
F src/pager.h e0acb095b3ad0bca48f2ab00c87346665643f64f F src/pager.h e0acb095b3ad0bca48f2ab00c87346665643f64f
F src/parse.y 58258759fabdd48f1d2561e276097290b1ea2680 F src/parse.y 58258759fabdd48f1d2561e276097290b1ea2680
F src/pragma.c 8883b4d34796efa315bdd0ec1b03f580ef1575b9 F src/pragma.c 4af4041a88d41421b8ff2e5574d82d7b9d1e35b1
F src/prepare.c 7639314c504f602d87730238c44ccddc4407ac60 F src/prepare.c 67ff283f7c71e32a91d8c843e758eb4bf68ab94e
F src/printf.c f47a2f4b5387cd2ebb12e9117a1a5d6bd9a2b812 F src/printf.c f47a2f4b5387cd2ebb12e9117a1a5d6bd9a2b812
F src/random.c ff5e9a8cad790e2a51cd4d2e7737dc8540e09d1d F src/random.c ff5e9a8cad790e2a51cd4d2e7737dc8540e09d1d
F src/select.c 5b0ccd6688e61c0720efa45075a3f9da60180554 F src/select.c 7a78520fcd79daccd4e33721dbc977d2e5c5d95a
F src/shell.c 66b073375efbdee19045e7e0cd38b85f9aff71da F src/shell.c 66b073375efbdee19045e7e0cd38b85f9aff71da
F src/sqlite.h.in ba3a29daa6a16e054191ccb384a981964e882a1d F src/sqlite.h.in ba3a29daa6a16e054191ccb384a981964e882a1d
F src/sqliteInt.h a9b187e8621cd3c20c7ef6c0716d77cbed716d4d F src/sqliteInt.h 5117ce283868de7010e62ec1a80ce1162575c184
F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316 F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316
F src/tclsqlite.c 0386460a4c017d49d729d44cdcb3fb3ffb2503e0 F src/tclsqlite.c 0386460a4c017d49d729d44cdcb3fb3ffb2503e0
F src/test1.c 988dbac66c3ca92d69fbe0283d77e86cd6f73ce8 F src/test1.c 988dbac66c3ca92d69fbe0283d77e86cd6f73ce8
@@ -80,21 +80,21 @@ F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f
F src/test6.c cb811391ec0b7c75f29e545d4820a9cf19f3637e F src/test6.c cb811391ec0b7c75f29e545d4820a9cf19f3637e
F src/test_async.c a383aed2753e47d2ac4b5397c43f6ac216a237ae F src/test_async.c a383aed2753e47d2ac4b5397c43f6ac216a237ae
F src/tokenize.c 7a3a3d3cc734f684a77c4dfd09eb46fcee25394c F src/tokenize.c 7a3a3d3cc734f684a77c4dfd09eb46fcee25394c
F src/trigger.c 2925ba96d964d9b717e74006bf7e64b8a6b70d97 F src/trigger.c 858c0a4974035b002fd2192399c6076ac7b75e1f
F src/update.c ec8e540617b116725b5a55c8d6b4db8bc67fdd7d F src/update.c c72e9cbbc0adf8d728c1c39ace03d4adb29b5cfb
F src/utf.c b7bffac4260177ae7f83c01d025fe0f5ed70ce71 F src/utf.c b7bffac4260177ae7f83c01d025fe0f5ed70ce71
F src/util.c a690bbf549fc5c465384f624e90c009935b6d18b F src/util.c a690bbf549fc5c465384f624e90c009935b6d18b
F src/vacuum.c fbfdd3967fd34e2f260fafed88dcbf3c10856b94 F src/vacuum.c fbfdd3967fd34e2f260fafed88dcbf3c10856b94
F src/vdbe.c 99bfdfda01747e11f74cb891312a669023ab355a F src/vdbe.c ad844cc360807ad00e513d17d5a7cf8d844cb86c
F src/vdbe.h 8729a4ee16ff9aeab2af9667df3cf300ff978e13 F src/vdbe.h 8729a4ee16ff9aeab2af9667df3cf300ff978e13
F src/vdbeInt.h 9b78ba00cc006bff17e04a54ba3ded9fc7810a10 F src/vdbeInt.h 9b78ba00cc006bff17e04a54ba3ded9fc7810a10
F src/vdbeapi.c b270b680cbc5d20b5a1abfdb08339667985df94e F src/vdbeapi.c b270b680cbc5d20b5a1abfdb08339667985df94e
F src/vdbeaux.c 7d55232a7e8c21899bbfc9ba3bd2469eb32faaf0 F src/vdbeaux.c 7d55232a7e8c21899bbfc9ba3bd2469eb32faaf0
F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
F src/vdbemem.c deba8d6e3727643924b210a8c531a496c2b8d386 F src/vdbemem.c deba8d6e3727643924b210a8c531a496c2b8d386
F src/where.c 0296a20c2e2a39c0cb785efe471fd1958a5259f3 F src/where.c 3ec45076e7cce523aad34eaf9bd119237b56942a
F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42 F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42
F test/all.test 55706917a12cd616440d50c35323747b4a9f03c3 F test/all.test 18602f8cd97d5741574dee7288e1f5f427f12825
F test/alter.test b94b640063e725d062b2997bd2810ac39195c718 F test/alter.test b94b640063e725d062b2997bd2810ac39195c718
F test/alter2.test cc0b8832e4e98605dbc26910efd4bb89abe59cb2 F test/alter2.test cc0b8832e4e98605dbc26910efd4bb89abe59cb2
F test/alter3.test 6e144ea3dcc395afcc28e794bb532be83dc8fdcb F test/alter3.test 6e144ea3dcc395afcc28e794bb532be83dc8fdcb
@@ -145,7 +145,7 @@ F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
F test/date.test 30ca15e608a45d868fd419c901795382efe27020 F test/date.test 30ca15e608a45d868fd419c901795382efe27020
F test/default.test 252298e42a680146b1dd64f563b95bdf088d94fb F test/default.test 252298e42a680146b1dd64f563b95bdf088d94fb
F test/delete.test 525a6953bc3978780cae35f3eaf1027cf4ce887d F test/delete.test 525a6953bc3978780cae35f3eaf1027cf4ce887d
F test/delete2.test d97a85b487f0ad2b391026ffade125549351f5cf F test/delete2.test d20b08733243f1890079f3b48f2356fbb62212b2
F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
F test/descidx1.test 466a9b87f3a7682156916dcbd5b77be55fd39d75 F test/descidx1.test 466a9b87f3a7682156916dcbd5b77be55fd39d75
F test/descidx2.test f9f73c562932b81207faa525cd52acdfd2fc0482 F test/descidx2.test f9f73c562932b81207faa525cd52acdfd2fc0482
@@ -221,7 +221,7 @@ F test/select4.test c239f516aa31f42f2ef7c6d7cd01105f08f934ca
F test/select5.test 07a90ab3c7e3f0a241a9cdea1d997b2c8a89ff0b F test/select5.test 07a90ab3c7e3f0a241a9cdea1d997b2c8a89ff0b
F test/select6.test f459a19bdac0501c4d3eb1a4df4b7a76f1bb8ad4 F test/select6.test f459a19bdac0501c4d3eb1a4df4b7a76f1bb8ad4
F test/select7.test 1bf795b948c133a15a2a5e99d3270e652ec58ce6 F test/select7.test 1bf795b948c133a15a2a5e99d3270e652ec58ce6
F test/shared.test 1f68f8aecf5064d1da2fb2f316bbb6e70054f08e F test/shared.test aa054381c8fe21d7f46dc1d460ac85f675297b26
F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5 F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5
F test/subquery.test e6de53332c0301b3cfa34edc3f3cd5fa1e859efd F test/subquery.test e6de53332c0301b3cfa34edc3f3cd5fa1e859efd
F test/subselect.test 2d13fb7f450db3595adcdd24079a0dd1d2d6abc2 F test/subselect.test 2d13fb7f450db3595adcdd24079a0dd1d2d6abc2
@@ -230,7 +230,7 @@ F test/table.test 6dc0dfa44dd429520e0e5a0c5e55025f730e9403
F test/tableapi.test 6a66d58b37d46dc0f2b3c7d4bd2617d209399bd1 F test/tableapi.test 6a66d58b37d46dc0f2b3c7d4bd2617d209399bd1
F test/tclsqlite.test 19578d32a7692311918caf0ae3521d19525bcb62 F test/tclsqlite.test 19578d32a7692311918caf0ae3521d19525bcb62
F test/temptable.test 7927261befdbc7b0a7ffebb85ecc70a74fa7b15b F test/temptable.test 7927261befdbc7b0a7ffebb85ecc70a74fa7b15b
F test/tester.tcl 9ab7dc33002fae680bf5f9a117a2747b01662069 F test/tester.tcl 58dcfde5265c3c46e05ae2af4accaa29f0b44d91
F test/thread1.test 776c9e459b75ba905193b351926ac4019b049f35 F test/thread1.test 776c9e459b75ba905193b351926ac4019b049f35
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
F test/threadtest2.c 97a830d53c24c42290501fdfba4a6e5bdd34748b F test/threadtest2.c 97a830d53c24c42290501fdfba4a6e5bdd34748b
@@ -327,6 +327,7 @@ F www/optimizing.tcl f0b2538988d1bbad16cbfe63ec6e8f48c9eb04e5
F www/optoverview.tcl 815df406a38c9f69b27d37e8f7ede004c6d9f19e F www/optoverview.tcl 815df406a38c9f69b27d37e8f7ede004c6d9f19e
F www/pragma.tcl 44f7b665ca598ad24724f35991653638a36a6e3f F www/pragma.tcl 44f7b665ca598ad24724f35991653638a36a6e3f
F www/quickstart.tcl 2f3daf8038e82a102e1e8cc877aafa7a413f5f11 F www/quickstart.tcl 2f3daf8038e82a102e1e8cc877aafa7a413f5f11
F www/sharedcache.tcl c42098d1436bcb54ec7f08d07c2e75316e2dde68
F www/speed.tcl 656ed5be8cc9d536353e1a96927b925634a62933 F www/speed.tcl 656ed5be8cc9d536353e1a96927b925634a62933
F www/sqlite.tcl a883ed7b47371d31d471e6aea5ed1f972ae8e1be F www/sqlite.tcl a883ed7b47371d31d471e6aea5ed1f972ae8e1be
F www/support.tcl 7961ce16290692578d783bb11f0dc8391a9be9c3 F www/support.tcl 7961ce16290692578d783bb11f0dc8391a9be9c3
@@ -335,7 +336,7 @@ F www/tclsqlite.tcl ddcf912ea48695603c8ed7efb29f0812ef8d1b49
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
P d0e3d466094f7b2f74ed7ebb324e5024ea8faa6f P cb9095ac52e76926f274678ef55ebb9df4b9fcac
R a1f52f5cef7ff73c69c22ea9cb8c2ecf R a8dde2ab271da0ac5619066fd6022d05
U drh U danielk1977
Z 0221548bd916e287249dc78e673eb685 Z 0d100378c01552c949846ec200b31003

View File

@@ -1 +1 @@
cb9095ac52e76926f274678ef55ebb9df4b9fcac deeda0dc06c1595aedd8d06a0c4e88a8abf78cf7

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that used to generate VDBE code ** This file contains C code routines that used to generate VDBE code
** that implements the ALTER TABLE command. ** 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 "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@@ -177,9 +177,16 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
Trigger *pTrig; Trigger *pTrig;
char *zWhere = 0; char *zWhere = 0;
char *tmp = 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 ){ for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){
if( pTrig->iDb==1 ){ if( pTrig->pSchema==pTempSchema ){
if( !zWhere ){ if( !zWhere ){
zWhere = sqlite3MPrintf("name=%Q", pTrig->name); zWhere = sqlite3MPrintf("name=%Q", pTrig->name);
}else{ }else{
@@ -204,20 +211,22 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
Vdbe *v; Vdbe *v;
char *zWhere; char *zWhere;
int iDb; int iDb; /* Index of database containing pTab */
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
Trigger *pTrig; Trigger *pTrig;
#endif #endif
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
if( !v ) return; if( !v ) return;
iDb = pTab->iDb; iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
assert( iDb>=0 );
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
/* Drop any table triggers from the internal schema. */ /* Drop any table triggers from the internal schema. */
for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){ for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){
assert( pTrig->iDb==iDb || pTrig->iDb==1 ); int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
sqlite3VdbeOp3(v, OP_DropTrigger, pTrig->iDb, 0, pTrig->name, 0); assert( iTrigDb==iDb || iTrigDb==1 );
sqlite3VdbeOp3(v, OP_DropTrigger, iTrigDb, 0, pTrig->name, 0);
} }
#endif #endif
@@ -263,7 +272,7 @@ void sqlite3AlterRenameTable(
pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase); pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase);
if( !pTab ) goto exit_rename_table; if( !pTab ) goto exit_rename_table;
iDb = pTab->iDb; iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
zDb = db->aDb[iDb].zName; zDb = db->aDb[iDb].zName;
/* Get a NULL terminated version of the new table name. */ /* Get a NULL terminated version of the new table name. */
@@ -390,7 +399,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
pNew = pParse->pNewTable; pNew = pParse->pNewTable;
assert( pNew ); assert( pNew );
iDb = pNew->iDb; iDb = sqlite3SchemaToIndex(pParse->db, pNew->pSchema);
zDb = pParse->db->aDb[iDb].zName; zDb = pParse->db->aDb[iDb].zName;
zTab = pNew->zName; zTab = pNew->zName;
pCol = &pNew->aCol[pNew->nCol-1]; pCol = &pNew->aCol[pNew->nCol-1];
@@ -490,7 +499,6 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
int i; int i;
int nAlloc; int nAlloc;
/* Look up the table being altered. */ /* Look up the table being altered. */
assert( pParse->pNewTable==0 ); assert( pParse->pNewTable==0 );
if( sqlite3Tsd()->mallocFailed ) goto exit_begin_add_column; if( sqlite3Tsd()->mallocFailed ) goto exit_begin_add_column;
@@ -504,7 +512,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
} }
assert( pTab->addColOffset>0 ); 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 /* Put a copy of the Table struct in Parse.pNewTable for the
** sqlite3AddColumn() function and friends to modify. ** sqlite3AddColumn() function and friends to modify.
@@ -529,7 +537,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
pCol->zType = 0; pCol->zType = 0;
pCol->pDflt = 0; pCol->pDflt = 0;
} }
pNew->iDb = iDb; pNew->pSchema = pParse->db->aDb[iDb].pSchema;
pNew->addColOffset = pTab->addColOffset; pNew->addColOffset = pTab->addColOffset;
pNew->nRef = 1; pNew->nRef = 1;

View File

@@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** This file contains code associated with the ANALYZE command. ** 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 #ifndef SQLITE_OMIT_ANALYZE
#include "sqliteInt.h" #include "sqliteInt.h"
@@ -86,6 +86,7 @@ static void analyzeOneTable(
int topOfLoop; /* The top of the loop */ int topOfLoop; /* The top of the loop */
int endOfLoop; /* The end of the loop */ int endOfLoop; /* The end of the loop */
int addr; /* The address of an instruction */ int addr; /* The address of an instruction */
int iDb; /* Index of database containing pTab */
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
if( pTab==0 || pTab->pIndex==0 ){ if( pTab==0 || pTab->pIndex==0 ){
@@ -93,9 +94,11 @@ static void analyzeOneTable(
return; return;
} }
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
assert( iDb>=0 );
#ifndef SQLITE_OMIT_AUTHORIZATION #ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0,
pParse->db->aDb[pTab->iDb].zName ) ){ pParse->db->aDb[iDb].zName ) ){
return; return;
} }
#endif #endif
@@ -104,7 +107,8 @@ static void analyzeOneTable(
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
/* Open a cursor to the index to be analyzed /* 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)); VdbeComment((v, "# %s", pIdx->zName));
sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum,
(char*)&pIdx->keyInfo, P3_KEYINFO); (char*)&pIdx->keyInfo, P3_KEYINFO);
@@ -215,6 +219,7 @@ static void loadAnalysis(Parse *pParse, int iDb){
*/ */
static void analyzeDatabase(Parse *pParse, int iDb){ static void analyzeDatabase(Parse *pParse, int iDb){
sqlite3 *db = pParse->db; sqlite3 *db = pParse->db;
DbSchema *pSchema = db->aDb[iDb].pSchema; /* Schema of database iDb */
HashElem *k; HashElem *k;
int iStatCur; int iStatCur;
int iMem; int iMem;
@@ -223,7 +228,7 @@ static void analyzeDatabase(Parse *pParse, int iDb){
iStatCur = pParse->nTab++; iStatCur = pParse->nTab++;
openStatTable(pParse, iDb, iStatCur, 0); openStatTable(pParse, iDb, iStatCur, 0);
iMem = pParse->nMem; 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); Table *pTab = (Table*)sqliteHashData(k);
analyzeOneTable(pParse, pTab, iStatCur, iMem); analyzeOneTable(pParse, pTab, iStatCur, iMem);
} }
@@ -239,7 +244,7 @@ static void analyzeTable(Parse *pParse, Table *pTab){
int iStatCur; int iStatCur;
assert( pTab!=0 ); assert( pTab!=0 );
iDb = pTab->iDb; iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab++; iStatCur = pParse->nTab++;
openStatTable(pParse, iDb, iStatCur, pTab->zName); openStatTable(pParse, iDb, iStatCur, pTab->zName);
@@ -361,7 +366,7 @@ void sqlite3AnalysisLoad(sqlite3 *db, int iDb){
char *zSql; char *zSql;
/* Clear any prior statistics */ /* 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); Index *pIdx = sqliteHashData(i);
sqlite3DefaultRowEst(pIdx); sqlite3DefaultRowEst(pIdx);
} }

View File

@@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands. ** 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" #include "sqliteInt.h"
@@ -116,16 +116,21 @@ static void attachFunc(
db->aDb = aNew; db->aDb = aNew;
aNew = &db->aDb[db->nDb++]; aNew = &db->aDb[db->nDb++];
memset(aNew, 0, sizeof(*aNew)); memset(aNew, 0, sizeof(*aNew));
sqlite3HashInit(&aNew->tblHash, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0); /* Open the database file. If the btree is successfully opened, use
sqlite3HashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0); ** it to obtain the database schema. At this point the schema may
sqlite3HashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1); ** 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->zName = sqliteStrDup(zName);
aNew->safety_level = 3; aNew->safety_level = 3;
/* Open the database file */
rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt);
#if SQLITE_HAS_CODEC #if SQLITE_HAS_CODEC
{ {
extern int sqlite3CodecAttach(sqlite3*, int, void*, int); extern int sqlite3CodecAttach(sqlite3*, int, void*, int);

View File

@@ -14,7 +14,7 @@
** systems that do not need this facility may omit it by recompiling ** systems that do not need this facility may omit it by recompiling
** the library with -DSQLITE_OMIT_AUTHORIZATION=1 ** 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" #include "sqliteInt.h"
@@ -112,10 +112,12 @@ void sqlite3AuthRead(
int iSrc; /* Index in pTabList->a[] of table being read */ int iSrc; /* Index in pTabList->a[] of table being read */
const char *zDBase; /* Name of database being accessed */ const char *zDBase; /* Name of database being accessed */
TriggerStack *pStack; /* The stack of current triggers */ TriggerStack *pStack; /* The stack of current triggers */
int iDb; /* The index of the database the expression refers to */
if( db->xAuth==0 ) return; if( db->xAuth==0 ) return;
if( pExpr->op==TK_AS ) return; if( pExpr->op==TK_AS ) return;
assert( pExpr->op==TK_COLUMN ); assert( pExpr->op==TK_COLUMN );
iDb = sqlite3SchemaToIndex(pParse->db, pExpr->pSchema);
for(iSrc=0; pTabList && iSrc<pTabList->nSrc; iSrc++){ for(iSrc=0; pTabList && iSrc<pTabList->nSrc; iSrc++){
if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break; if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break;
} }
@@ -140,14 +142,14 @@ void sqlite3AuthRead(
}else{ }else{
zCol = "ROWID"; zCol = "ROWID";
} }
assert( pExpr->iDb<db->nDb ); assert( iDb<db->nDb );
zDBase = db->aDb[pExpr->iDb].zName; zDBase = db->aDb[iDb].zName;
rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase, rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase,
pParse->zAuthContext); pParse->zAuthContext);
if( rc==SQLITE_IGNORE ){ if( rc==SQLITE_IGNORE ){
pExpr->op = TK_NULL; pExpr->op = TK_NULL;
}else if( rc==SQLITE_DENY ){ }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", sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",
zDBase, pTab->zName, zCol); zDBase, pTab->zName, zCol);
}else{ }else{

View File

@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give. ** 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. ** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to ** For a detailed discussion of BTrees, refer to
@@ -349,6 +349,8 @@ struct BtShared {
int nRef; /* Number of references to this structure */ int nRef; /* Number of references to this structure */
int nTransaction; /* Number of open transactions (read + write) */ int nTransaction; /* Number of open transactions (read + write) */
BtLock *pLock; /* List of locks held on this shared-btree struct */ 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[] */ int idx; /* Index of the entry in pPage->aCell[] */
CellInfo info; /* A parse of the cell we are pointing at */ CellInfo info; /* A parse of the cell we are pointing at */
u8 wrFlag; /* True if writable */ 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 ** The TRACE macro will print high-level status information about the
** btree operation when the global variable sqlite3_btree_trace is ** 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 ** 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 table level locks. If the library is compiled with the
** shared-cache feature disabled, then there is only ever one user ** shared-cache feature disabled, then there is only ever one user
** of each BtShared structure and so this locking is not required. ** of each BtShared structure and so this locking is not necessary.
** So define the three interface functions as no-ops. ** So define the lock related functions as no-ops.
*/ */
#define queryTableLock(a,b,c) SQLITE_OK #define queryTableLock(a,b,c) SQLITE_OK
#define lockTable(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 #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 ** 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 ** (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; BtShared *pBt = p->pBt;
BtLock *pIter; 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){ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
if( pIter->pBtree!=p && pIter->iTable==iTab && 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_BUSY;
} }
} }
}
return SQLITE_OK; return SQLITE_OK;
} }
@@ -502,8 +655,27 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
BtLock *pLock = 0; BtLock *pLock = 0;
BtLock *pIter; 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) ); 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. */ /* First search the list for an existing lock on this table. */
for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
if( pIter->iTable==iTable && pIter->pBtree==p ){ 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){ static void unlockAllTables(Btree *p){
BtLock **ppIter = &p->pBt->pLock; 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 ){ while( *ppIter ){
BtLock *pLock = *ppIter; BtLock *pLock = *ppIter;
if( pLock->pBtree==p ){ if( pLock->pBtree==p ){
@@ -680,27 +859,6 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
#endif /* SQLITE_OMIT_AUTOVACUUM */ #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 ** 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 ** the page, 1 means the second cell, and so forth) return a pointer
@@ -1386,7 +1544,9 @@ int sqlite3BtreeOpen(
int rc; int rc;
int nReserve; int nReserve;
unsigned char zDbHeader[100]; unsigned char zDbHeader[100];
#ifndef SQLITE_OMIT_SHARED_CACHE
SqliteTsd *pTsd = sqlite3Tsd(); SqliteTsd *pTsd = sqlite3Tsd();
#endif
/* Set the variable isMemdb to true for an in-memory database, or /* Set the variable isMemdb to true for an in-memory database, or
** false for a file-based database. This symbol is only required if ** false for a file-based database. This symbol is only required if
@@ -1501,8 +1661,8 @@ int sqlite3BtreeOpen(
pBt->pNext = pTsd->pBtree; pBt->pNext = pTsd->pBtree;
pTsd->pBtree = pBt; pTsd->pBtree = pBt;
} }
pBt->nRef = 1;
#endif #endif
pBt->nRef = 1;
*ppBtree = p; *ppBtree = p;
return SQLITE_OK; return SQLITE_OK;
} }
@@ -1511,10 +1671,13 @@ int sqlite3BtreeOpen(
** Close an open database and invalidate all cursors. ** Close an open database and invalidate all cursors.
*/ */
int sqlite3BtreeClose(Btree *p){ int sqlite3BtreeClose(Btree *p){
SqliteTsd *pTsd = sqlite3Tsd();
BtShared *pBt = p->pBt; BtShared *pBt = p->pBt;
BtCursor *pCur; BtCursor *pCur;
#ifndef SQLITE_OMIT_SHARED_CACHE
SqliteTsd *pTsd = sqlite3Tsd();
#endif
/* Drop any table-locks */ /* Drop any table-locks */
unlockAllTables(p); unlockAllTables(p);
@@ -1556,6 +1719,9 @@ int sqlite3BtreeClose(Btree *p){
/* Close the pager and free the shared-btree structure */ /* Close the pager and free the shared-btree structure */
assert( !pBt->pCursor ); assert( !pBt->pCursor );
sqlite3pager_close(pBt->pPager); sqlite3pager_close(pBt->pPager);
if( pBt->xFreeSchema && pBt->pSchema ){
pBt->xFreeSchema(pBt->pSchema);
}
sqliteFree(pBt); sqliteFree(pBt);
return SQLITE_OK; return SQLITE_OK;
} }
@@ -2323,7 +2489,7 @@ void sqlite3BtreeCursorList(Btree *p){
sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n", sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
pCur, pCur->pgnoRoot, zMode, pCur, pCur->pgnoRoot, zMode,
pPage ? pPage->pgno : 0, pCur->idx, pPage ? pPage->pgno : 0, pCur->idx,
pCur->isValid ? "" : " eof" (pCur->eState==CURSOR_VALID) ? "" : " eof"
); );
} }
} }
@@ -2532,7 +2698,7 @@ int sqlite3BtreeCursor(
return rc; return rc;
} }
} }
pCur = sqliteMallocRaw( sizeof(*pCur) ); pCur = sqliteMalloc( sizeof(*pCur) );
if( pCur==0 ){ if( pCur==0 ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
goto create_cursor_exception; goto create_cursor_exception;
@@ -2571,7 +2737,7 @@ int sqlite3BtreeCursor(
} }
pCur->pPrev = 0; pCur->pPrev = 0;
pBt->pCursor = pCur; pBt->pCursor = pCur;
pCur->isValid = 0; pCur->eState = CURSOR_INVALID;
*ppCur = pCur; *ppCur = pCur;
return SQLITE_OK; return SQLITE_OK;
@@ -2604,6 +2770,7 @@ void sqlite3BtreeSetCompare(
*/ */
int sqlite3BtreeCloseCursor(BtCursor *pCur){ int sqlite3BtreeCloseCursor(BtCursor *pCur){
BtShared *pBt = pCur->pBtree->pBt; BtShared *pBt = pCur->pBtree->pBt;
restoreCursorPosition(pCur, 0);
if( pCur->pPrev ){ if( pCur->pPrev ){
pCur->pPrev->pNext = pCur->pNext; pCur->pPrev->pNext = pCur->pNext;
}else{ }else{
@@ -2670,13 +2837,17 @@ static void getCellInfo(BtCursor *pCur){
** itself, not the number of bytes in the key. ** itself, not the number of bytes in the key.
*/ */
int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){ 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; *pSize = 0;
}else{ }else{
getCellInfo(pCur); getCellInfo(pCur);
*pSize = pCur->info.nKey; *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. ** the database is empty) then *pSize is set to 0.
*/ */
int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ 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. */ /* Not pointing at a valid entry - set *pSize to 0. */
*pSize = 0; *pSize = 0;
}else{ }else{
getCellInfo(pCur); getCellInfo(pCur);
*pSize = pCur->info.nData; *pSize = pCur->info.nData;
} }
return SQLITE_OK; }
return rc;
} }
/* /*
@@ -2722,7 +2897,7 @@ static int getPayload(
u32 nKey; u32 nKey;
assert( pCur!=0 && pCur->pPage!=0 ); assert( pCur!=0 && pCur->pPage!=0 );
assert( pCur->isValid ); assert( pCur->eState==CURSOR_VALID );
pBt = pCur->pBtree->pBt; pBt = pCur->pBtree->pBt;
pPage = pCur->pPage; pPage = pCur->pPage;
pageIntegrity(pPage); pageIntegrity(pPage);
@@ -2798,14 +2973,18 @@ static int getPayload(
** the available payload. ** the available payload.
*/ */
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ 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 ); assert( pCur->pPage!=0 );
if( pCur->pPage->intKey ){ if( pCur->pPage->intKey ){
return SQLITE_CORRUPT_BKPT; return SQLITE_CORRUPT_BKPT;
} }
assert( pCur->pPage->intKey==0 ); assert( pCur->pPage->intKey==0 );
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell ); 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. ** the available payload.
*/ */
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ 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->pPage!=0 );
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell ); 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; int nLocal;
assert( pCur!=0 && pCur->pPage!=0 ); assert( pCur!=0 && pCur->pPage!=0 );
assert( pCur->isValid ); assert( pCur->eState==CURSOR_VALID );
pPage = pCur->pPage; pPage = pCur->pPage;
pageIntegrity(pPage); pageIntegrity(pPage);
assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); 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. ** in the common case where no overflow pages are used.
*/ */
const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){ const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){
if( pCur->eState==CURSOR_VALID ){
return (const void*)fetchPayload(pCur, pAmt, 0); return (const void*)fetchPayload(pCur, pAmt, 0);
}
return 0;
} }
const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){ const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
if( pCur->eState==CURSOR_VALID ){
return (const void*)fetchPayload(pCur, pAmt, 1); return (const void*)fetchPayload(pCur, pAmt, 1);
}
return 0;
} }
@@ -2909,7 +3098,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
MemPage *pOldPage; MemPage *pOldPage;
BtShared *pBt = pCur->pBtree->pBt; BtShared *pBt = pCur->pBtree->pBt;
assert( pCur->isValid ); assert( pCur->eState==CURSOR_VALID );
rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage); rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
if( rc ) return rc; if( rc ) return rc;
pageIntegrity(pNewPage); pageIntegrity(pNewPage);
@@ -2956,7 +3145,7 @@ static void moveToParent(BtCursor *pCur){
MemPage *pPage; MemPage *pPage;
int idxParent; int idxParent;
assert( pCur->isValid ); assert( pCur->eState==CURSOR_VALID );
pPage = pCur->pPage; pPage = pCur->pPage;
assert( pPage!=0 ); assert( pPage!=0 );
assert( !isRootPage(pPage) ); assert( !isRootPage(pPage) );
@@ -2981,9 +3170,10 @@ static int moveToRoot(BtCursor *pCur){
int rc; int rc;
BtShared *pBt = pCur->pBtree->pBt; BtShared *pBt = pCur->pBtree->pBt;
restoreCursorPosition(pCur, 0);
rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0); rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0);
if( rc ){ if( rc ){
pCur->isValid = 0; pCur->eState = CURSOR_INVALID;
return rc; return rc;
} }
releasePage(pCur->pPage); releasePage(pCur->pPage);
@@ -2996,10 +3186,10 @@ static int moveToRoot(BtCursor *pCur){
assert( pRoot->pgno==1 ); assert( pRoot->pgno==1 );
subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]);
assert( subpage>0 ); assert( subpage>0 );
pCur->isValid = 1; pCur->eState = CURSOR_VALID;
rc = moveToChild(pCur, subpage); rc = moveToChild(pCur, subpage);
} }
pCur->isValid = pCur->pPage->nCell>0; pCur->eState = ((pCur->pPage->nCell>0)?CURSOR_VALID:CURSOR_INVALID);
return rc; return rc;
} }
@@ -3012,7 +3202,7 @@ static int moveToLeftmost(BtCursor *pCur){
int rc; int rc;
MemPage *pPage; MemPage *pPage;
assert( pCur->isValid ); assert( pCur->eState==CURSOR_VALID );
while( !(pPage = pCur->pPage)->leaf ){ while( !(pPage = pCur->pPage)->leaf ){
assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
pgno = get4byte(findCell(pPage, pCur->idx)); pgno = get4byte(findCell(pPage, pCur->idx));
@@ -3034,7 +3224,7 @@ static int moveToRightmost(BtCursor *pCur){
int rc; int rc;
MemPage *pPage; MemPage *pPage;
assert( pCur->isValid ); assert( pCur->eState==CURSOR_VALID );
while( !(pPage = pCur->pPage)->leaf ){ while( !(pPage = pCur->pPage)->leaf ){
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
pCur->idx = pPage->nCell; pCur->idx = pPage->nCell;
@@ -3054,7 +3244,7 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
int rc; int rc;
rc = moveToRoot(pCur); rc = moveToRoot(pCur);
if( rc ) return rc; if( rc ) return rc;
if( pCur->isValid==0 ){ if( pCur->eState==CURSOR_INVALID ){
assert( pCur->pPage->nCell==0 ); assert( pCur->pPage->nCell==0 );
*pRes = 1; *pRes = 1;
return SQLITE_OK; return SQLITE_OK;
@@ -3073,12 +3263,12 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
int rc; int rc;
rc = moveToRoot(pCur); rc = moveToRoot(pCur);
if( rc ) return rc; if( rc ) return rc;
if( pCur->isValid==0 ){ if( CURSOR_INVALID==pCur->eState ){
assert( pCur->pPage->nCell==0 ); assert( pCur->pPage->nCell==0 );
*pRes = 1; *pRes = 1;
return SQLITE_OK; return SQLITE_OK;
} }
assert( pCur->isValid ); assert( pCur->eState==CURSOR_VALID );
*pRes = 0; *pRes = 0;
rc = moveToRightmost(pCur); rc = moveToRightmost(pCur);
return rc; return rc;
@@ -3117,7 +3307,7 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
if( rc ) return rc; if( rc ) return rc;
assert( pCur->pPage ); assert( pCur->pPage );
assert( pCur->pPage->isInit ); assert( pCur->pPage->isInit );
if( pCur->isValid==0 ){ if( pCur->eState==CURSOR_INVALID ){
*pRes = -1; *pRes = -1;
assert( pCur->pPage->nCell==0 ); assert( pCur->pPage->nCell==0 );
return SQLITE_OK; 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. ** the first entry. TRUE is also returned if the table is empty.
*/ */
int sqlite3BtreeEof(BtCursor *pCur){ 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; int rc;
MemPage *pPage = pCur->pPage; 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 ); assert( pRes!=0 );
if( pCur->isValid==0 ){ if( CURSOR_INVALID==pCur->eState ){
*pRes = 1; *pRes = 1;
return SQLITE_OK; return SQLITE_OK;
} }
@@ -3243,7 +3450,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
do{ do{
if( isRootPage(pPage) ){ if( isRootPage(pPage) ){
*pRes = 1; *pRes = 1;
pCur->isValid = 0; pCur->eState = CURSOR_INVALID;
return SQLITE_OK; return SQLITE_OK;
} }
moveToParent(pCur); moveToParent(pCur);
@@ -3275,7 +3482,21 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
int rc; int rc;
Pgno pgno; Pgno pgno;
MemPage *pPage; 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; *pRes = 1;
return SQLITE_OK; return SQLITE_OK;
} }
@@ -3291,7 +3512,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
}else{ }else{
while( pCur->idx==0 ){ while( pCur->idx==0 ){
if( isRootPage(pPage) ){ if( isRootPage(pPage) ){
pCur->isValid = 0; pCur->eState = CURSOR_INVALID;
*pRes = 1; *pRes = 1;
return SQLITE_OK; return SQLITE_OK;
} }
@@ -4887,7 +5108,9 @@ static int balance(MemPage *pPage, int insert){
static int checkReadLocks(BtShared *pBt, Pgno pgnoRoot, BtCursor *pExclude){ static int checkReadLocks(BtShared *pBt, Pgno pgnoRoot, BtCursor *pExclude){
BtCursor *p; BtCursor *p;
for(p=pBt->pCursor; p; p=p->pNext){ 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->pgnoRoot!=pgnoRoot || p==pExclude ) continue;
if( p->wrFlag==0 && flags&SQLITE_ReadUncommitted ) continue;
if( p->wrFlag==0 ) return SQLITE_LOCKED; if( p->wrFlag==0 ) return SQLITE_LOCKED;
if( p->pPage->pgno!=p->pgnoRoot ){ if( p->pPage->pgno!=p->pgnoRoot ){
moveToRoot(p); moveToRoot(p);
@@ -4929,6 +5152,14 @@ int sqlite3BtreeInsert(
if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){ if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */ 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); rc = sqlite3BtreeMoveto(pCur, pKey, nKey, &loc);
if( rc ) return rc; if( rc ) return rc;
pPage = pCur->pPage; pPage = pCur->pPage;
@@ -4946,7 +5177,7 @@ int sqlite3BtreeInsert(
if( rc ) goto end_insert; if( rc ) goto end_insert;
assert( szNew==cellSizePtr(pPage, newCell) ); assert( szNew==cellSizePtr(pPage, newCell) );
assert( szNew<=MX_CELL_SIZE(pBt) ); assert( szNew<=MX_CELL_SIZE(pBt) );
if( loc==0 && pCur->isValid ){ if( loc==0 && CURSOR_VALID==pCur->eState ){
int szOld; int szOld;
assert( pCur->idx>=0 && pCur->idx<pPage->nCell ); assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
oldCell = findCell(pPage, pCur->idx); oldCell = findCell(pPage, pCur->idx);
@@ -5003,8 +5234,19 @@ int sqlite3BtreeDelete(BtCursor *pCur){
if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){ if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */ 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 /* Locate the cell within it's page and leave pCell pointing to the
** data. The clearCell() call frees any overflow pages associated with 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; unsigned char *pP1;
BtShared *pBt = p->pBt; 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 ); assert( idx>=0 && idx<=15 );
rc = sqlite3pager_get(pBt->pPager, 1, (void**)&pP1); rc = sqlite3pager_get(pBt->pPager, 1, (void**)&pP1);
if( rc ) return rc; if( rc ) return rc;
@@ -5440,7 +5692,9 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
if( idx==4 && *pMeta>0 ) pBt->readOnly = 1; if( idx==4 && *pMeta>0 ) pBt->readOnly = 1;
#endif #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. ** is currently pointing to.
*/ */
int sqlite3BtreeFlags(BtCursor *pCur){ int sqlite3BtreeFlags(BtCursor *pCur){
/* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call
** restoreCursorPosition() here.
*/
MemPage *pPage = pCur->pPage; MemPage *pPage = pCur->pPage;
return pPage ? pPage->aData[pPage->hdrOffset] : 0; return pPage ? pPage->aData[pPage->hdrOffset] : 0;
} }
@@ -5601,6 +5858,11 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
MemPage *pPage = pCur->pPage; MemPage *pPage = pCur->pPage;
BtCursor tmpCur; BtCursor tmpCur;
int rc = restoreCursorPosition(pCur, 1);
if( rc!=SQLITE_OK ){
return rc;
}
pageIntegrity(pPage); pageIntegrity(pPage);
assert( pPage->isInit ); assert( pPage->isInit );
getTempCursor(pCur, &tmpCur); getTempCursor(pCur, &tmpCur);
@@ -6196,6 +6458,31 @@ int sqlite3BtreeSync(Btree *p, const char *zMaster){
return SQLITE_OK; 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 #ifndef SQLITE_OMIT_SHARED_CACHE
/* /*
** Enable the shared pager and schema features. ** Enable the shared pager and schema features.

View File

@@ -13,7 +13,7 @@
** subsystem. See comments in the source code for a detailed description ** subsystem. See comments in the source code for a detailed description
** of what each interface routine does. ** 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_ #ifndef _BTREE_H_
#define _BTREE_H_ #define _BTREE_H_
@@ -76,6 +76,7 @@ int sqlite3BtreeCreateTable(Btree*, int*, int flags);
int sqlite3BtreeIsInTrans(Btree*); int sqlite3BtreeIsInTrans(Btree*);
int sqlite3BtreeIsInStmt(Btree*); int sqlite3BtreeIsInStmt(Btree*);
int sqlite3BtreeSync(Btree*, const char *zMaster); int sqlite3BtreeSync(Btree*, const char *zMaster);
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
const char *sqlite3BtreeGetFilename(Btree *); const char *sqlite3BtreeGetFilename(Btree *);
const char *sqlite3BtreeGetDirname(Btree *); const char *sqlite3BtreeGetDirname(Btree *);

View File

@@ -22,7 +22,7 @@
** COMMIT ** COMMIT
** ROLLBACK ** 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 "sqliteInt.h"
#include <ctype.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++){ for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue; 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; if( p ) break;
} }
return p; return p;
@@ -227,8 +227,12 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
assert( (db->flags & SQLITE_Initialized) || db->init.busy ); assert( (db->flags & SQLITE_Initialized) || db->init.busy );
for(i=OMIT_TEMPDB; i<db->nDb; i++){ for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ 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; 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; if( p ) break;
} }
return p; return p;
@@ -252,10 +256,10 @@ static void freeIndex(Index *p){
*/ */
static void sqliteDeleteIndex(sqlite3 *db, Index *p){ static void sqliteDeleteIndex(sqlite3 *db, Index *p){
Index *pOld; Index *pOld;
const char *zName = p->zName;
assert( db!=0 && p->zName!=0 ); assert( db!=0 && zName!=0 );
pOld = sqlite3HashInsert(&db->aDb[p->iDb].idxHash, p->zName, pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName, strlen( zName)+1, 0);
strlen(p->zName)+1, 0);
assert( pOld==0 || pOld==p ); assert( pOld==0 || pOld==p );
freeIndex(p); freeIndex(p);
} }
@@ -269,9 +273,10 @@ static void sqliteDeleteIndex(sqlite3 *db, Index *p){
void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
Index *pIndex; Index *pIndex;
int len; int len;
Hash *pHash = &db->aDb[iDb].pSchema->idxHash;
len = strlen(zIdxName); len = strlen(zIdxName);
pIndex = sqlite3HashInsert(&db->aDb[iDb].idxHash, zIdxName, len+1, 0); pIndex = sqlite3HashInsert(pHash, zIdxName, len+1, 0);
if( pIndex ){ if( pIndex ){
if( pIndex->pTable->pIndex==pIndex ){ if( pIndex->pTable->pIndex==pIndex ){
pIndex->pTable->pIndex = pIndex->pNext; pIndex->pTable->pIndex = pIndex->pNext;
@@ -308,23 +313,25 @@ void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
db->flags &= ~SQLITE_Initialized; db->flags &= ~SQLITE_Initialized;
for(i=iDb; i<db->nDb; i++){ for(i=iDb; i<db->nDb; i++){
Db *pDb = &db->aDb[i]; Db *pDb = &db->aDb[i];
temp1 = pDb->tblHash; if( pDb->pSchema ){
temp2 = pDb->trigHash; temp1 = pDb->pSchema->tblHash;
sqlite3HashInit(&pDb->trigHash, SQLITE_HASH_STRING, 0); temp2 = pDb->pSchema->trigHash;
sqlite3HashClear(&pDb->aFKey); sqlite3HashInit(&pDb->pSchema->trigHash, SQLITE_HASH_STRING, 0);
sqlite3HashClear(&pDb->idxHash); sqlite3HashClear(&pDb->pSchema->aFKey);
sqlite3HashClear(&pDb->pSchema->idxHash);
for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){ for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
sqlite3DeleteTrigger((Trigger*)sqliteHashData(pElem)); sqlite3DeleteTrigger((Trigger*)sqliteHashData(pElem));
} }
sqlite3HashClear(&temp2); 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)){ for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
Table *pTab = sqliteHashData(pElem); Table *pTab = sqliteHashData(pElem);
sqlite3DeleteTable(db, pTab); sqlite3DeleteTable(db, pTab);
} }
sqlite3HashClear(&temp1); sqlite3HashClear(&temp1);
pDb->pSeqTab = 0; pDb->pSchema->pSeqTab = 0;
DbClearProperty(db, i, DB_SchemaLoaded); DbClearProperty(db, i, DB_SchemaLoaded);
}
if( iDb>0 ) return; if( iDb>0 ) return;
} }
assert( iDb==0 ); assert( iDb==0 );
@@ -433,7 +440,7 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
*/ */
for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
pNext = pIndex->pNext; pNext = pIndex->pNext;
assert( pIndex->iDb==pTable->iDb || (pTable->iDb==0 && pIndex->iDb==1) ); assert( pIndex->pSchema==pTable->pSchema );
sqliteDeleteIndex(db, pIndex); sqliteDeleteIndex(db, pIndex);
} }
@@ -443,8 +450,8 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
*/ */
for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){ for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){
pNextFKey = pFKey->pNextFrom; pNextFKey = pFKey->pNextFrom;
assert( pTable->iDb<db->nDb ); assert( sqlite3SchemaToIndex(db, pTable->pSchema)<db->nDb );
assert( sqlite3HashFind(&db->aDb[pTable->iDb].aFKey, assert( sqlite3HashFind(&pTable->pSchema->aFKey,
pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey ); pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey );
sqliteFree(pFKey); sqliteFree(pFKey);
} }
@@ -475,14 +482,14 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){
assert( iDb>=0 && iDb<db->nDb ); assert( iDb>=0 && iDb<db->nDb );
assert( zTabName && zTabName[0] ); assert( zTabName && zTabName[0] );
pDb = &db->aDb[iDb]; 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 ){ if( p ){
#ifndef SQLITE_OMIT_FOREIGN_KEY #ifndef SQLITE_OMIT_FOREIGN_KEY
for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){ for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){
int nTo = strlen(pF1->zTo) + 1; int nTo = strlen(pF1->zTo) + 1;
pF2 = sqlite3HashFind(&pDb->aFKey, pF1->zTo, nTo); pF2 = sqlite3HashFind(&pDb->pSchema->aFKey, pF1->zTo, nTo);
if( pF2==pF1 ){ if( pF2==pF1 ){
sqlite3HashInsert(&pDb->aFKey, pF1->zTo, nTo, pF1->pNextTo); sqlite3HashInsert(&pDb->pSchema->aFKey, pF1->zTo, nTo, pF1->pNextTo);
}else{ }else{
while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; } while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; }
if( pF2 ){ if( pF2 ){
@@ -734,7 +741,7 @@ void sqlite3StartTable(
pTable->aCol = 0; pTable->aCol = 0;
pTable->iPKey = -1; pTable->iPKey = -1;
pTable->pIndex = 0; pTable->pIndex = 0;
pTable->iDb = iDb; pTable->pSchema = db->aDb[iDb].pSchema;
pTable->nRef = 1; pTable->nRef = 1;
if( pParse->pNewTable ) sqlite3DeleteTable(db, pParse->pNewTable); if( pParse->pNewTable ) sqlite3DeleteTable(db, pParse->pNewTable);
pParse->pNewTable = pTable; pParse->pNewTable = pTable;
@@ -745,7 +752,7 @@ void sqlite3StartTable(
*/ */
#ifndef SQLITE_OMIT_AUTOINCREMENT #ifndef SQLITE_OMIT_AUTOINCREMENT
if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){
db->aDb[iDb].pSeqTab = pTable; pTable->pSchema->pSeqTab = pTable;
} }
#endif #endif
@@ -1179,7 +1186,7 @@ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){
** 1 chance in 2^32. So we're safe enough. ** 1 chance in 2^32. So we're safe enough.
*/ */
void sqlite3ChangeCookie(sqlite3 *db, Vdbe *v, int iDb){ 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); 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 ** table. Memory to hold the text of the statement is obtained
** from sqliteMalloc() and must be freed by the calling function. ** 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; int i, k, n;
char *zStmt; char *zStmt;
char *zSep, *zSep2, *zEnd, *z; char *zSep, *zSep2, *zEnd, *z;
@@ -1253,7 +1260,7 @@ static char *createTableStmt(Table *p){
n += 35 + 6*p->nCol; n += 35 + 6*p->nCol;
zStmt = sqliteMallocRaw( n ); zStmt = sqliteMallocRaw( n );
if( zStmt==0 ) return 0; 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); k = strlen(zStmt);
identPut(zStmt, &k, p->zName); identPut(zStmt, &k, p->zName);
zStmt[k++] = '('; zStmt[k++] = '(';
@@ -1300,6 +1307,7 @@ void sqlite3EndTable(
){ ){
Table *p; Table *p;
sqlite3 *db = pParse->db; sqlite3 *db = pParse->db;
int iDb;
if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite3Tsd()->mallocFailed ) { if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite3Tsd()->mallocFailed ) {
return; return;
@@ -1309,6 +1317,8 @@ void sqlite3EndTable(
assert( !db->init.busy || !pSelect ); assert( !db->init.busy || !pSelect );
iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
#ifndef SQLITE_OMIT_CHECK #ifndef SQLITE_OMIT_CHECK
/* Resolve names in all CHECK constraint expressions. /* Resolve names in all CHECK constraint expressions.
*/ */
@@ -1387,7 +1397,7 @@ void sqlite3EndTable(
if( pSelect ){ if( pSelect ){
Table *pSelTab; Table *pSelTab;
sqlite3VdbeAddOp(v, OP_Dup, 0, 0); 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); sqlite3VdbeAddOp(v, OP_OpenWrite, 1, 0);
pParse->nTab = 2; pParse->nTab = 2;
sqlite3Select(pParse, pSelect, SRT_Table, 1, 0, 0, 0, 0); sqlite3Select(pParse, pSelect, SRT_Table, 1, 0, 0, 0, 0);
@@ -1406,7 +1416,7 @@ void sqlite3EndTable(
/* Compute the complete text of the CREATE statement */ /* Compute the complete text of the CREATE statement */
if( pSelect ){ if( pSelect ){
zStmt = createTableStmt(p); zStmt = createTableStmt(p, p->pSchema==pParse->db->aDb[1].pSchema);
}else{ }else{
n = pEnd->z - pParse->sNameToken.z + 1; n = pEnd->z - pParse->sNameToken.z + 1;
zStmt = sqlite3MPrintf("CREATE %s %.*s", zType2, n, pParse->sNameToken.z); zStmt = sqlite3MPrintf("CREATE %s %.*s", zType2, n, pParse->sNameToken.z);
@@ -1422,22 +1432,22 @@ void sqlite3EndTable(
"UPDATE %Q.%s " "UPDATE %Q.%s "
"SET type='%s', name=%Q, tbl_name=%Q, rootpage=#0, sql=%Q " "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#0, sql=%Q "
"WHERE rowid=#1", "WHERE rowid=#1",
db->aDb[p->iDb].zName, SCHEMA_TABLE(p->iDb), db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
zType, zType,
p->zName, p->zName,
p->zName, p->zName,
zStmt zStmt
); );
sqliteFree(zStmt); sqliteFree(zStmt);
sqlite3ChangeCookie(db, v, p->iDb); sqlite3ChangeCookie(db, v, iDb);
#ifndef SQLITE_OMIT_AUTOINCREMENT #ifndef SQLITE_OMIT_AUTOINCREMENT
/* Check to see if we need to create an sqlite_sequence table for /* Check to see if we need to create an sqlite_sequence table for
** keeping track of autoincrement keys. ** keeping track of autoincrement keys.
*/ */
if( p->autoInc ){ if( p->autoInc ){
Db *pDb = &db->aDb[p->iDb]; Db *pDb = &db->aDb[iDb];
if( pDb->pSeqTab==0 ){ if( pDb->pSchema->pSeqTab==0 ){
sqlite3NestedParse(pParse, sqlite3NestedParse(pParse,
"CREATE TABLE %Q.sqlite_sequence(name,seq)", "CREATE TABLE %Q.sqlite_sequence(name,seq)",
pDb->zName pDb->zName
@@ -1447,7 +1457,7 @@ void sqlite3EndTable(
#endif #endif
/* Reparse everything to update our internal data structures */ /* 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); sqlite3MPrintf("tbl_name='%q'",p->zName), P3_DYNAMIC);
} }
@@ -1457,8 +1467,8 @@ void sqlite3EndTable(
if( db->init.busy && pParse->nErr==0 ){ if( db->init.busy && pParse->nErr==0 ){
Table *pOld; Table *pOld;
FKey *pFKey; FKey *pFKey;
Db *pDb = &db->aDb[p->iDb]; DbSchema *pSchema = p->pSchema;
pOld = sqlite3HashInsert(&pDb->tblHash, p->zName, strlen(p->zName)+1, p); pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, strlen(p->zName)+1,p);
if( pOld ){ if( pOld ){
assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ assert( p==pOld ); /* Malloc must have failed inside HashInsert() */
return; return;
@@ -1466,8 +1476,8 @@ void sqlite3EndTable(
#ifndef SQLITE_OMIT_FOREIGN_KEY #ifndef SQLITE_OMIT_FOREIGN_KEY
for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){ for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){
int nTo = strlen(pFKey->zTo) + 1; int nTo = strlen(pFKey->zTo) + 1;
pFKey->pNextTo = sqlite3HashFind(&pDb->aFKey, pFKey->zTo, nTo); pFKey->pNextTo = sqlite3HashFind(&pSchema->aFKey, pFKey->zTo, nTo);
sqlite3HashInsert(&pDb->aFKey, pFKey->zTo, nTo, pFKey); sqlite3HashInsert(&pSchema->aFKey, pFKey->zTo, nTo, pFKey);
} }
#endif #endif
pParse->pNewTable = 0; pParse->pNewTable = 0;
@@ -1502,6 +1512,7 @@ void sqlite3CreateView(
Token sEnd; Token sEnd;
DbFixer sFix; DbFixer sFix;
Token *pName; Token *pName;
int iDb;
if( pParse->nVar>0 ){ if( pParse->nVar>0 ){
sqlite3ErrorMsg(pParse, "parameters are not allowed in views"); sqlite3ErrorMsg(pParse, "parameters are not allowed in views");
@@ -1515,7 +1526,8 @@ void sqlite3CreateView(
return; return;
} }
sqlite3TwoPartName(pParse, pName1, pName2, &pName); 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) && sqlite3FixSelect(&sFix, pSelect)
){ ){
sqlite3SelectDelete(pSelect); sqlite3SelectDelete(pSelect);
@@ -1615,7 +1627,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
pSelTab->nCol = 0; pSelTab->nCol = 0;
pSelTab->aCol = 0; pSelTab->aCol = 0;
sqlite3DeleteTable(0, pSelTab); sqlite3DeleteTable(0, pSelTab);
DbSetProperty(pParse->db, pTable->iDb, DB_UnresetViews); pTable->pSchema->flags |= DB_UnresetViews;
}else{ }else{
pTable->nCol = 0; pTable->nCol = 0;
nErr++; nErr++;
@@ -1635,7 +1647,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
static void sqliteViewResetAll(sqlite3 *db, int idx){ static void sqliteViewResetAll(sqlite3 *db, int idx){
HashElem *i; HashElem *i;
if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; 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); Table *pTab = sqliteHashData(i);
if( pTab->pSelect ){ if( pTab->pSelect ){
sqliteResetColumnNames(pTab); sqliteResetColumnNames(pTab);
@@ -1656,15 +1668,18 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
#ifndef SQLITE_OMIT_AUTOVACUUM #ifndef SQLITE_OMIT_AUTOVACUUM
void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){ void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){
HashElem *pElem; 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); Table *pTab = sqliteHashData(pElem);
if( pTab->tnum==iFrom ){ if( pTab->tnum==iFrom ){
pTab->tnum = iTo; pTab->tnum = iTo;
return; 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); Index *pIdx = sqliteHashData(pElem);
if( pIdx->tnum==iFrom ){ if( pIdx->tnum==iFrom ){
pIdx->tnum = iTo; pIdx->tnum = iTo;
@@ -1741,15 +1756,19 @@ static void destroyTable(Parse *pParse, Table *pTab){
} }
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int iIdx = pIdx->tnum; int iIdx = pIdx->tnum;
assert( pIdx->iDb==pTab->iDb ); assert( pIdx->pSchema==pTab->pSchema );
if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){ if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){
iLargest = iIdx; iLargest = iIdx;
} }
} }
if( iLargest==0 ) return; if( iLargest==0 ){
destroyRootPage(pParse, iLargest, pTab->iDb); return;
}else{
int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
destroyRootPage(pParse, iLargest, iDb);
iDestroyed = iLargest; iDestroyed = iLargest;
} }
}
#endif #endif
} }
@@ -1773,13 +1792,13 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
} }
goto exit_drop_table; goto exit_drop_table;
} }
iDb = pTab->iDb; iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb>=0 && iDb<db->nDb ); assert( iDb>=0 && iDb<db->nDb );
#ifndef SQLITE_OMIT_AUTHORIZATION #ifndef SQLITE_OMIT_AUTHORIZATION
{ {
int code; int code;
const char *zTab = SCHEMA_TABLE(pTab->iDb); const char *zTab = SCHEMA_TABLE(iDb);
const char *zDb = db->aDb[pTab->iDb].zName; const char *zDb = db->aDb[iDb].zName;
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){ if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
goto exit_drop_table; goto exit_drop_table;
} }
@@ -1804,7 +1823,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
} }
} }
#endif #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); sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
goto exit_drop_table; goto exit_drop_table;
} }
@@ -1829,7 +1848,6 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
if( v ){ if( v ){
Trigger *pTrigger; Trigger *pTrigger;
int iDb = pTab->iDb;
Db *pDb = &db->aDb[iDb]; Db *pDb = &db->aDb[iDb];
sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3BeginWriteOperation(pParse, 0, iDb);
@@ -1839,7 +1857,8 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
*/ */
pTrigger = pTab->pTrigger; pTrigger = pTab->pTrigger;
while( 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); sqlite3DropTriggerPtr(pParse, pTrigger, 1);
pTrigger = pTrigger->pNext; pTrigger = pTrigger->pNext;
} }
@@ -2035,10 +2054,11 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
int addr1; /* Address of top of loop */ int addr1; /* Address of top of loop */
int tnum; /* Root page of index */ int tnum; /* Root page of index */
Vdbe *v; /* Generate code into this virtual machine */ Vdbe *v; /* Generate code into this virtual machine */
int iDb = sqlite3SchemaToIndex(pParse->db, pIndex->pSchema);
#ifndef SQLITE_OMIT_AUTHORIZATION #ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0, if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0,
pParse->db->aDb[pIndex->iDb].zName ) ){ pParse->db->aDb[iDb].zName ) ){
return; return;
} }
#endif #endif
@@ -2058,12 +2078,12 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
tnum = 0; tnum = 0;
}else{ }else{
tnum = pIndex->tnum; 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, sqlite3VdbeOp3(v, OP_OpenWrite, iIdx, tnum,
(char*)&pIndex->keyInfo, P3_KEYINFO); (char*)&pIndex->keyInfo, P3_KEYINFO);
sqlite3OpenTableForReading(v, iTab, pTab); sqlite3OpenTableForReading(v, iTab, iDb, pTab);
addr1 = sqlite3VdbeAddOp(v, OP_Rewind, iTab, 0); addr1 = sqlite3VdbeAddOp(v, OP_Rewind, iTab, 0);
sqlite3GenerateIndexKey(v, pIndex, iTab); sqlite3GenerateIndexKey(v, pIndex, iTab);
if( pIndex->onError!=OE_None ){ if( pIndex->onError!=OE_None ){
@@ -2142,7 +2162,7 @@ void sqlite3CreateIndex(
** is a temp table. If so, set the database to 1. ** is a temp table. If so, set the database to 1.
*/ */
pTab = sqlite3SrcListLookup(pParse, pTblName); 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; iDb = 1;
} }
#endif #endif
@@ -2157,12 +2177,12 @@ void sqlite3CreateIndex(
pTab = sqlite3LocateTable(pParse, pTblName->a[0].zName, pTab = sqlite3LocateTable(pParse, pTblName->a[0].zName,
pTblName->a[0].zDatabase); pTblName->a[0].zDatabase);
if( !pTab ) goto exit_create_index; if( !pTab ) goto exit_create_index;
assert( iDb==pTab->iDb ); assert( db->aDb[iDb].pSchema==pTab->pSchema );
}else{ }else{
assert( pName==0 ); assert( pName==0 );
pTab = pParse->pNewTable; pTab = pParse->pNewTable;
if( !pTab ) goto exit_create_index; if( !pTab ) goto exit_create_index;
iDb = pTab->iDb; iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
} }
pDb = &db->aDb[iDb]; pDb = &db->aDb[iDb];
@@ -2266,11 +2286,11 @@ void sqlite3CreateIndex(
pIndex->nColumn = pList->nExpr; pIndex->nColumn = pList->nExpr;
pIndex->onError = onError; pIndex->onError = onError;
pIndex->autoIndex = pName==0; 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 /* 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 */ sortOrderMask = -1; /* Honor DESC */
}else{ }else{
sortOrderMask = 0; /* Ignore DESC */ sortOrderMask = 0; /* Ignore DESC */
@@ -2365,7 +2385,7 @@ void sqlite3CreateIndex(
*/ */
if( db->init.busy ){ if( db->init.busy ){
Index *p; Index *p;
p = sqlite3HashInsert(&db->aDb[pIndex->iDb].idxHash, p = sqlite3HashInsert(&pIndex->pSchema->idxHash,
pIndex->zName, strlen(pIndex->zName)+1, pIndex); pIndex->zName, strlen(pIndex->zName)+1, pIndex);
if( p ){ if( p ){
assert( p==pIndex ); /* Malloc must have failed */ assert( p==pIndex ); /* Malloc must have failed */
@@ -2533,6 +2553,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
Index *pIndex; Index *pIndex;
Vdbe *v; Vdbe *v;
sqlite3 *db = pParse->db; sqlite3 *db = pParse->db;
int iDb;
if( pParse->nErr || sqlite3Tsd()->mallocFailed ){ if( pParse->nErr || sqlite3Tsd()->mallocFailed ){
goto exit_drop_index; goto exit_drop_index;
@@ -2554,16 +2575,17 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
"or PRIMARY KEY constraint cannot be dropped", 0); "or PRIMARY KEY constraint cannot be dropped", 0);
goto exit_drop_index; goto exit_drop_index;
} }
iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
#ifndef SQLITE_OMIT_AUTHORIZATION #ifndef SQLITE_OMIT_AUTHORIZATION
{ {
int code = SQLITE_DROP_INDEX; int code = SQLITE_DROP_INDEX;
Table *pTab = pIndex->pTable; Table *pTab = pIndex->pTable;
const char *zDb = db->aDb[pIndex->iDb].zName; const char *zDb = db->aDb[iDb].zName;
const char *zTab = SCHEMA_TABLE(pIndex->iDb); const char *zTab = SCHEMA_TABLE(iDb);
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
goto exit_drop_index; 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) ){ if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){
goto exit_drop_index; 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 */ /* Generate code to remove the index and from the master table */
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
if( v ){ if( v ){
int iDb = pIndex->iDb;
sqlite3NestedParse(pParse, sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE name=%Q", "DELETE FROM %Q.%s WHERE name=%Q",
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
@@ -2853,6 +2874,12 @@ static int sqlite3OpenTempDatabase(Parse *pParse){
pParse->rc = rc; pParse->rc = rc;
return 1; return 1;
} }
/*
db->aDb[1].pSchema = sqlite3SchemaGet(db->aDb[1].pBt);
if( !db->aDb[1].pSchema ){
return SQLITE_NOMEM;
}
*/
if( db->flags & !db->autoCommit ){ if( db->flags & !db->autoCommit ){
rc = sqlite3BtreeBeginTrans(db->aDb[1].pBt, 1); rc = sqlite3BtreeBeginTrans(db->aDb[1].pBt, 1);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
@@ -2906,7 +2933,7 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
mask = 1<<iDb; mask = 1<<iDb;
if( (pParse->cookieMask & mask)==0 ){ if( (pParse->cookieMask & mask)==0 ){
pParse->cookieMask |= mask; 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 ){ if( !OMIT_TEMPDB && iDb==1 ){
sqlite3OpenTempDatabase(pParse); sqlite3OpenTempDatabase(pParse);
} }
@@ -2971,7 +2998,8 @@ static void reindexTable(Parse *pParse, Table *pTab, CollSeq *pColl){
for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){
if( pColl==0 || collationMatch(pColl,pIndex) ){ 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); 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++){ for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){
if( pDb==0 ) continue; 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); pTab = (Table*)sqliteHashData(k);
reindexTable(pParse, pTab, pColl); reindexTable(pParse, pTab, pColl);
} }

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements. ** 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" #include "sqliteInt.h"
@@ -62,9 +62,10 @@ int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
void sqlite3OpenTableForReading( void sqlite3OpenTableForReading(
Vdbe *v, /* Generate code into this VDBE */ Vdbe *v, /* Generate code into this VDBE */
int iCur, /* The cursor number of the table */ int iCur, /* The cursor number of the table */
int iDb, /* The database index in sqlite3.aDb[] */
Table *pTab /* The table to be opened */ 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)); VdbeComment((v, "# %s", pTab->zName));
sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum); sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol); sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
@@ -95,6 +96,7 @@ void sqlite3DeleteFrom(
AuthContext sContext; /* Authorization context */ AuthContext sContext; /* Authorization context */
int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */ int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */
NameContext sNC; /* Name context to resolve expressions in */ NameContext sNC; /* Name context to resolve expressions in */
int iDb;
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to delete from a view */ int isView; /* True if attempting to delete from a view */
@@ -134,8 +136,9 @@ void sqlite3DeleteFrom(
if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){
goto delete_from_cleanup; goto delete_from_cleanup;
} }
assert( pTab->iDb<db->nDb ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
zDb = db->aDb[pTab->iDb].zName; assert( iDb<db->nDb );
zDb = db->aDb[iDb].zName;
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
goto delete_from_cleanup; goto delete_from_cleanup;
} }
@@ -176,7 +179,7 @@ void sqlite3DeleteFrom(
goto delete_from_cleanup; goto delete_from_cleanup;
} }
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); 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 /* If we are trying to delete from a view, realize that view into
** a ephemeral table. ** a ephemeral table.
@@ -205,7 +208,7 @@ void sqlite3DeleteFrom(
int endOfLoop = sqlite3VdbeMakeLabel(v); int endOfLoop = sqlite3VdbeMakeLabel(v);
int addr; int addr;
if( !isView ){ if( !isView ){
sqlite3OpenTableForReading(v, iCur, pTab); sqlite3OpenTableForReading(v, iCur, iDb, pTab);
} }
sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2); sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2);
addr = sqlite3VdbeAddOp(v, OP_AddImm, 1, 0); addr = sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
@@ -214,12 +217,13 @@ void sqlite3DeleteFrom(
sqlite3VdbeAddOp(v, OP_Close, iCur, 0); sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
} }
if( !isView ){ if( !isView ){
sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb); sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, iDb);
if( !pParse->nested ){ if( !pParse->nested ){
sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC); sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
} }
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ 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); addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
if( !isView ){ if( !isView ){
sqlite3VdbeAddOp(v, OP_Dup, 0, 0); sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
sqlite3OpenTableForReading(v, iCur, pTab); sqlite3OpenTableForReading(v, iCur, iDb, pTab);
} }
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0); sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);

View File

@@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and ** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite. ** 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 "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@@ -843,6 +843,7 @@ static int lookupName(
if( pSrcList ){ if( pSrcList ){
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
Table *pTab = pItem->pTab; Table *pTab = pItem->pTab;
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
Column *pCol; Column *pCol;
if( pTab==0 ) continue; if( pTab==0 ) continue;
@@ -854,14 +855,14 @@ static int lookupName(
}else{ }else{
char *zTabName = pTab->zName; char *zTabName = pTab->zName;
if( zTabName==0 || sqlite3StrICmp(zTabName, zTab)!=0 ) continue; 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; continue;
} }
} }
} }
if( 0==(cntTab++) ){ if( 0==(cntTab++) ){
pExpr->iTable = pItem->iCursor; pExpr->iTable = pItem->iCursor;
pExpr->iDb = pTab->iDb; pExpr->pSchema = pTab->pSchema;
pMatch = pItem; pMatch = pItem;
} }
for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
@@ -870,7 +871,7 @@ static int lookupName(
cnt++; cnt++;
pExpr->iTable = pItem->iCursor; pExpr->iTable = pItem->iCursor;
pMatch = pItem; pMatch = pItem;
pExpr->iDb = pTab->iDb; pExpr->pSchema = pTab->pSchema;
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
pExpr->iColumn = j==pTab->iPKey ? -1 : j; pExpr->iColumn = j==pTab->iPKey ? -1 : j;
pExpr->affinity = pTab->aCol[j].affinity; pExpr->affinity = pTab->aCol[j].affinity;
@@ -921,7 +922,7 @@ static int lookupName(
int j; int j;
Column *pCol = pTab->aCol; Column *pCol = pTab->aCol;
pExpr->iDb = pTab->iDb; pExpr->pSchema = pTab->pSchema;
cntTab++; cntTab++;
for(j=0; j < pTab->nCol; j++, pCol++) { for(j=0; j < pTab->nCol; j++, pCol++) {
if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ if( sqlite3StrICmp(pCol->zName, zCol)==0 ){

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite. ** 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" #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. ** 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; int i;
struct SrcList_item *pItem; struct SrcList_item *pItem;
if( p->pSrc==0 ) return 0; if( p->pSrc==0 ) return 0;
for(i=0, pItem=p->pSrc->a; i<p->pSrc->nSrc; i++, pItem++){ for(i=0, pItem=p->pSrc->a; i<p->pSrc->nSrc; i++, pItem++){
if( pItem->pSelect ){ if( pItem->pSelect ){
if( selectReadsTable(pItem->pSelect, iDb, iTab) ) return 1; if( selectReadsTable(pItem->pSelect, pSchema, iTab) ) return 1;
}else{ }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; return 0;
@@ -214,6 +214,7 @@ void sqlite3Insert(
int newIdx = -1; /* Cursor for the NEW table */ int newIdx = -1; /* Cursor for the NEW table */
Db *pDb; /* The database containing table being inserted into */ Db *pDb; /* The database containing table being inserted into */
int counterMem = 0; /* Memory cell holding AUTOINCREMENT counter */ int counterMem = 0; /* Memory cell holding AUTOINCREMENT counter */
int iDb;
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to insert into a view */ int isView; /* True if attempting to insert into a view */
@@ -236,8 +237,9 @@ void sqlite3Insert(
if( pTab==0 ){ if( pTab==0 ){
goto insert_cleanup; goto insert_cleanup;
} }
assert( pTab->iDb<db->nDb ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
pDb = &db->aDb[pTab->iDb]; assert( iDb<db->nDb );
pDb = &db->aDb[iDb];
zDb = pDb->zName; zDb = pDb->zName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){ if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
goto insert_cleanup; goto insert_cleanup;
@@ -285,7 +287,7 @@ void sqlite3Insert(
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
if( v==0 ) goto insert_cleanup; if( v==0 ) goto insert_cleanup;
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); 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 there are row triggers, allocate a temp table for new.* references. */
if( triggers_exist ){ if( triggers_exist ){
@@ -303,8 +305,8 @@ void sqlite3Insert(
int base = sqlite3VdbeCurrentAddr(v); int base = sqlite3VdbeCurrentAddr(v);
counterRowid = pParse->nMem++; counterRowid = pParse->nMem++;
counterMem = pParse->nMem++; counterMem = pParse->nMem++;
sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pDb->pSeqTab->tnum); sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pDb->pSchema->pSeqTab->tnum);
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, 2); sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, 2);
sqlite3VdbeAddOp(v, OP_Rewind, iCur, base+13); sqlite3VdbeAddOp(v, OP_Rewind, iCur, base+13);
sqlite3VdbeAddOp(v, OP_Column, iCur, 0); sqlite3VdbeAddOp(v, OP_Column, iCur, 0);
@@ -353,7 +355,7 @@ void sqlite3Insert(
** of the tables being read by the SELECT statement. Also use a ** of the tables being read by the SELECT statement. Also use a
** temp table in the case of row triggers. ** 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; useTempTable = 1;
} }
@@ -684,8 +686,8 @@ void sqlite3Insert(
if( pTab->autoInc ){ if( pTab->autoInc ){
int iCur = pParse->nTab; int iCur = pParse->nTab;
int base = sqlite3VdbeCurrentAddr(v); int base = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
sqlite3VdbeAddOp(v, OP_OpenWrite, iCur, pDb->pSeqTab->tnum); sqlite3VdbeAddOp(v, OP_OpenWrite, iCur, pDb->pSchema->pSeqTab->tnum);
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, 2); sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, 2);
sqlite3VdbeAddOp(v, OP_MemLoad, counterRowid, 0); sqlite3VdbeAddOp(v, OP_MemLoad, counterRowid, 0);
sqlite3VdbeAddOp(v, OP_NotNull, -1, base+7); sqlite3VdbeAddOp(v, OP_NotNull, -1, base+7);
@@ -1104,15 +1106,17 @@ void sqlite3OpenTableAndIndices(
int op /* OP_OpenRead or OP_OpenWrite */ int op /* OP_OpenRead or OP_OpenWrite */
){ ){
int i; int i;
int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
Index *pIdx; Index *pIdx;
Vdbe *v = sqlite3GetVdbe(pParse); Vdbe *v = sqlite3GetVdbe(pParse);
assert( v!=0 ); assert( v!=0 );
sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
VdbeComment((v, "# %s", pTab->zName)); VdbeComment((v, "# %s", pTab->zName));
sqlite3VdbeAddOp(v, op, base, pTab->tnum); sqlite3VdbeAddOp(v, op, base, pTab->tnum);
sqlite3VdbeAddOp(v, OP_SetNumColumns, base, pTab->nCol); sqlite3VdbeAddOp(v, OP_SetNumColumns, base, pTab->nCol);
for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ 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)); VdbeComment((v, "# %s", pIdx->zName));
sqlite3VdbeOp3(v, op, i+base, pIdx->tnum, sqlite3VdbeOp3(v, op, i+base, pIdx->tnum,
(char*)&pIdx->keyInfo, P3_KEYINFO); (char*)&pIdx->keyInfo, P3_KEYINFO);

View File

@@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be ** other files are for internal use by SQLite and should not be
** accessed by users of the library. ** 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 "sqliteInt.h"
#include "os.h" #include "os.h"
@@ -95,6 +95,53 @@ int sqlite3_total_changes(sqlite3 *db){
return db->nTotalChange; 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 ** Close an existing SQLite database
*/ */
@@ -185,6 +232,7 @@ int sqlite3_close(sqlite3 *db){
#endif #endif
db->magic = SQLITE_MAGIC_ERROR; db->magic = SQLITE_MAGIC_ERROR;
sqliteFree(db->aDb[1].pSchema);
sqliteFree(db); sqliteFree(db);
sqlite3MallocAllow(); sqlite3MallocAllow();
return SQLITE_OK; return SQLITE_OK;
@@ -639,7 +687,7 @@ int sqlite3BtreeFactory(
#endif /* SQLITE_OMIT_MEMORYDB */ #endif /* SQLITE_OMIT_MEMORYDB */
} }
rc = sqlite3BtreeOpen(zFilename, db, ppBtree, btree_flags); rc = sqlite3BtreeOpen(zFilename, (sqlite3 *)db, ppBtree, btree_flags);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
sqlite3BtreeSetBusyHandler(*ppBtree, (void*)&db->busyHandler); sqlite3BtreeSetBusyHandler(*ppBtree, (void*)&db->busyHandler);
sqlite3BtreeSetCacheSize(*ppBtree, nCache); sqlite3BtreeSetCacheSize(*ppBtree, nCache);
@@ -732,7 +780,7 @@ static int openDatabase(
sqlite3 **ppDb /* OUT: Returned database handle */ sqlite3 **ppDb /* OUT: Returned database handle */
){ ){
sqlite3 *db; sqlite3 *db;
int rc, i; int rc;
CollSeq *pColl; CollSeq *pColl;
assert( !sqlite3Tsd()->mallocFailed ); assert( !sqlite3Tsd()->mallocFailed );
@@ -749,12 +797,15 @@ static int openDatabase(
db->flags |= SQLITE_ShortColNames; db->flags |= SQLITE_ShortColNames;
sqlite3HashInit(&db->aFunc, SQLITE_HASH_STRING, 0); sqlite3HashInit(&db->aFunc, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&db->aCollSeq, SQLITE_HASH_STRING, 0); sqlite3HashInit(&db->aCollSeq, SQLITE_HASH_STRING, 0);
#if 0
for(i=0; i<db->nDb; i++){ for(i=0; i<db->nDb; i++){
sqlite3HashInit(&db->aDb[i].tblHash, SQLITE_HASH_STRING, 0); sqlite3HashInit(&db->aDb[i].tblHash, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&db->aDb[i].idxHash, 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].trigHash, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&db->aDb[i].aFKey, SQLITE_HASH_STRING, 1); sqlite3HashInit(&db->aDb[i].aFKey, SQLITE_HASH_STRING, 1);
} }
#endif
/* Add the default collation sequence BINARY. BINARY works for both UTF-8 /* 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 ** 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; db->magic = SQLITE_MAGIC_CLOSED;
goto opendb_out; 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 /* The default safety_level for the main database is 'full'; for the temp
** database it is 'NONE'. This matches the pager layer defaults. ** database it is 'NONE'. This matches the pager layer defaults.
@@ -800,7 +853,6 @@ static int openDatabase(
db->aDb[1].safety_level = 1; db->aDb[1].safety_level = 1;
#endif #endif
/* Register all built-in functions, but do not attempt to read the /* Register all built-in functions, but do not attempt to read the
** database schema yet. This is delayed until the first time the database ** database schema yet. This is delayed until the first time the database
** is accessed. ** is accessed.

View File

@@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** This file contains code used to implement the PRAGMA command. ** 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 "sqliteInt.h"
#include "os.h" #include "os.h"
@@ -157,6 +157,10 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
/* The following is VERY experimental */ /* The following is VERY experimental */
{ "writable_schema", SQLITE_WriteSchema }, { "writable_schema", SQLITE_WriteSchema },
{ "omit_readlock", SQLITE_NoReadlock }, { "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; int i;
const struct sPragmaType *p; const struct sPragmaType *p;
@@ -650,6 +654,7 @@ void sqlite3Pragma(
/* Do an integrity check on each database file */ /* Do an integrity check on each database file */
for(i=0; i<db->nDb; i++){ for(i=0; i<db->nDb; i++){
HashElem *x; HashElem *x;
Hash *pTbls;
int cnt = 0; int cnt = 0;
if( OMIT_TEMPDB && i==1 ) continue; if( OMIT_TEMPDB && i==1 ) continue;
@@ -658,7 +663,8 @@ void sqlite3Pragma(
/* Do an integrity check of the B-Tree /* 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); Table *pTab = sqliteHashData(x);
Index *pIdx; Index *pIdx;
sqlite3VdbeAddOp(v, OP_Integer, pTab->tnum, 0); sqlite3VdbeAddOp(v, OP_Integer, pTab->tnum, 0);
@@ -685,7 +691,7 @@ void sqlite3Pragma(
/* Make sure all the indices are constructed correctly. /* Make sure all the indices are constructed correctly.
*/ */
sqlite3CodeVerifySchema(pParse, i); 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); Table *pTab = sqliteHashData(x);
Index *pIdx; Index *pIdx;
int loopTop; int loopTop;

View File

@@ -13,7 +13,7 @@
** interface, and routines that contribute to loading the database schema ** interface, and routines that contribute to loading the database schema
** from disk. ** 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 "sqliteInt.h"
#include "os.h" #include "os.h"
@@ -49,6 +49,10 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){
sqlite3 *db = pData->db; sqlite3 *db = pData->db;
int iDb; int iDb;
if( sqlite3Tsd()->mallocFailed ){
return SQLITE_NOMEM;
}
assert( argc==4 ); assert( argc==4 );
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
if( argv[1]==0 || argv[3]==0 ){ 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 ); 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 /* zMasterSchema and zInitScript are set to point at the master schema
** and initialisation script appropriate for the database being ** and initialisation script appropriate for the database being
** initialised. zMasterName is the name of the master table. ** initialised. zMasterName is the name of the master table.
@@ -226,7 +246,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
}else{ }else{
memset(meta, 0, sizeof(meta)); 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 /* If opening a non-empty database, check the text encoding. For the
** main database, set sqlite3.enc to the encoding of the main database. ** 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==3 Version 3.1.4. // ditto but with non-NULL defaults
** file_format==4 Version 3.3.0. // DESC indices. Boolean constants ** file_format==4 Version 3.3.0. // DESC indices. Boolean constants
*/ */
pDb->file_format = meta[1]; pDb->pSchema->file_format = meta[1];
if( pDb->file_format==0 ){ if( pDb->pSchema->file_format==0 ){
pDb->file_format = 1; pDb->pSchema->file_format = 1;
} }
if( pDb->file_format>SQLITE_MAX_FILE_FORMAT ){ if( pDb->pSchema->file_format>SQLITE_MAX_FILE_FORMAT ){
sqlite3BtreeCloseCursor(curMain); sqlite3BtreeCloseCursor(curMain);
sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0); sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
return SQLITE_ERROR; return SQLITE_ERROR;
@@ -394,7 +414,7 @@ static int schemaIsValid(sqlite3 *db){
rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, 0, &curTemp); rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, 0, &curTemp);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&cookie); 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; allOk = 0;
} }
sqlite3BtreeCloseCursor(curTemp); sqlite3BtreeCloseCursor(curTemp);

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite. ** 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" #include "sqliteInt.h"
@@ -2202,6 +2202,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
struct ExprList_item eListItem; struct ExprList_item eListItem;
SrcList *pSrc; SrcList *pSrc;
int brk; int brk;
int iDb;
/* Check to see if this query is a simple min() or max() query. Return /* Check to see if this query is a simple min() or max() query. Return
** zero if it is not. ** 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 ** the min() or max() is on the INTEGER PRIMARY KEY, then find the first
** or last entry in the main table. ** 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; base = pSrc->a[0].iCursor;
brk = sqlite3VdbeMakeLabel(v); brk = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, brk); computeLimitRegisters(pParse, p, brk);
if( pSrc->a[0].pSelect==0 ){ if( pSrc->a[0].pSelect==0 ){
sqlite3OpenTableForReading(v, base, pTab); sqlite3OpenTableForReading(v, base, iDb, pTab);
} }
if( pIdx==0 ){ if( pIdx==0 ){
sqlite3VdbeAddOp(v, seekOp, base, 0); sqlite3VdbeAddOp(v, seekOp, base, 0);
@@ -2281,7 +2283,8 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
*/ */
int iIdx; int iIdx;
iIdx = pParse->nTab++; 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, sqlite3VdbeOp3(v, OP_OpenRead, iIdx, pIdx->tnum,
(char*)&pIdx->keyInfo, P3_KEYINFO); (char*)&pIdx->keyInfo, P3_KEYINFO);
if( seekOp==OP_Rewind ){ if( seekOp==OP_Rewind ){

View File

@@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** Internal interface definitions for SQLite. ** 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_ #ifndef _SQLITEINT_H_
#define _SQLITEINT_H_ #define _SQLITEINT_H_
@@ -336,6 +336,7 @@ typedef struct AuthContext AuthContext;
typedef struct CollSeq CollSeq; typedef struct CollSeq CollSeq;
typedef struct Column Column; typedef struct Column Column;
typedef struct Db Db; typedef struct Db Db;
typedef struct DbSchema DbSchema;
typedef struct Expr Expr; typedef struct Expr Expr;
typedef struct ExprList ExprList; typedef struct ExprList ExprList;
typedef struct FKey FKey; typedef struct FKey FKey;
@@ -367,29 +368,36 @@ typedef struct WhereLevel WhereLevel;
struct Db { struct Db {
char *zName; /* Name of this database */ char *zName; /* Name of this database */
Btree *pBt; /* The B*Tree structure for this database file */ 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 */ int schema_cookie; /* Database schema version number for this file */
Hash tblHash; /* All tables indexed by name */ Hash tblHash; /* All tables indexed by name */
Hash idxHash; /* All (named) indices indexed by name */ Hash idxHash; /* All (named) indices indexed by name */
Hash trigHash; /* All triggers indexed by name */ Hash trigHash; /* All triggers indexed by name */
Hash aFKey; /* Foreign keys indexed by to-table */ 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 */ Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */
void *pAux; /* Auxiliary data. Usually NULL */ u8 file_format; /* Schema format version for this file */
void (*xFreeAux)(void*); /* Routine to free pAux */ u16 flags; /* Flags associated with this schema */
}; };
/* /*
** These macros can be used to test, set, or clear bits in the ** These macros can be used to test, set, or clear bits in the
** Db.flags field. ** Db.flags field.
*/ */
#define DbHasProperty(D,I,P) (((D)->aDb[I].flags&(P))==(P)) #define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))==(P))
#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].flags&(P))!=0) #define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))!=0)
#define DbSetProperty(D,I,P) (D)->aDb[I].flags|=(P) #define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->flags|=(P)
#define DbClearProperty(D,I,P) (D)->aDb[I].flags&=~(P) #define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->flags&=~(P)
/* /*
** Allowed values for the DB.flags field. ** Allowed values for the DB.flags field.
@@ -518,6 +526,7 @@ struct sqlite3 {
#define SQLITE_NoReadlock 0x00001000 /* Readlocks are omitted when #define SQLITE_NoReadlock 0x00001000 /* Readlocks are omitted when
** accessing read-only databases */ ** accessing read-only databases */
#define SQLITE_IgnoreChecks 0x00002000 /* Do not enforce check constraints */ #define SQLITE_IgnoreChecks 0x00002000 /* Do not enforce check constraints */
#define SQLITE_ReadUncommitted 0x00004000 /* For shared-cache mode */
/* /*
** Possible values for the sqlite.magic field. ** Possible values for the sqlite.magic field.
@@ -672,7 +681,7 @@ struct Table {
int tnum; /* Root BTree node for this table (see note above) */ int tnum; /* Root BTree node for this table (see note above) */
Select *pSelect; /* NULL for tables. Points to definition if a view. */ 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 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 isTransient; /* True if automatically deleted when VDBE finishes */
u8 hasPrimKey; /* True if there exists a primary key */ u8 hasPrimKey; /* True if there exists a primary key */
u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
@@ -687,6 +696,7 @@ struct Table {
#ifndef SQLITE_OMIT_ALTERTABLE #ifndef SQLITE_OMIT_ALTERTABLE
int addColOffset; /* Offset in CREATE TABLE statement to add a new column */ int addColOffset; /* Offset in CREATE TABLE statement to add a new column */
#endif #endif
DbSchema *pSchema;
}; };
/* /*
@@ -822,9 +832,10 @@ struct Index {
int tnum; /* Page containing root of this index in database file */ int tnum; /* Page containing root of this index in database file */
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ 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 */ char *zColAff; /* String defining the affinity of each column */
Index *pNext; /* The next index associated with the same table */ Index *pNext; /* The next index associated with the same table */
DbSchema *pSchema;
KeyInfo keyInfo; /* Info on how to order keys. MUST BE LAST */ KeyInfo keyInfo; /* Info on how to order keys. MUST BE LAST */
}; };
@@ -934,7 +945,7 @@ struct AggInfo {
struct Expr { struct Expr {
u8 op; /* Operation performed by this node */ u8 op; /* Operation performed by this node */
char affinity; /* The affinity of the column or 0 if not a column */ 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 */ u8 flags; /* Various flags. See below */
CollSeq *pColl; /* The collation type of the column or 0 */ CollSeq *pColl; /* The collation type of the column or 0 */
Expr *pLeft, *pRight; /* Left and right subnodes */ Expr *pLeft, *pRight; /* Left and right subnodes */
@@ -950,6 +961,7 @@ struct Expr {
Select *pSelect; /* When the expression is a sub-select. Also the Select *pSelect; /* When the expression is a sub-select. Also the
** right side of "<expr> IN (<select>)" */ ** right side of "<expr> IN (<select>)" */
Table *pTab; /* Table for OP_Column expressions. */ Table *pTab; /* Table for OP_Column expressions. */
DbSchema *pSchema;
}; };
/* /*
@@ -1277,7 +1289,7 @@ struct AuthContext {
struct Trigger { struct Trigger {
char *name; /* The name of the trigger */ char *name; /* The name of the trigger */
char *table; /* The table or view to which the trigger applies */ 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 iTabDb; /* Database containing Trigger.table */
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
@@ -1286,7 +1298,7 @@ struct Trigger {
the <column-list> is stored here */ the <column-list> is stored here */
int foreach; /* One of TK_ROW or TK_STATEMENT */ int foreach; /* One of TK_ROW or TK_STATEMENT */
Token nameToken; /* Token containing zName. Use during parsing only */ Token nameToken; /* Token containing zName. Use during parsing only */
DbSchema *pSchema;
TriggerStep *step_list; /* Link list of trigger program steps */ TriggerStep *step_list; /* Link list of trigger program steps */
Trigger *pNext; /* Next trigger associated with the table */ Trigger *pNext; /* Next trigger associated with the table */
}; };
@@ -1527,7 +1539,7 @@ void sqlite3SelectDelete(Select*);
void sqlite3SelectUnbind(Select*); void sqlite3SelectUnbind(Select*);
Table *sqlite3SrcListLookup(Parse*, SrcList*); Table *sqlite3SrcListLookup(Parse*, SrcList*);
int sqlite3IsReadOnly(Parse*, Table*, int); 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 sqlite3OpenTable(Vdbe*, int iCur, Table*, int);
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
@@ -1700,6 +1712,9 @@ int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
SqliteTsd *sqlite3Tsd(); SqliteTsd *sqlite3Tsd();
void sqlite3AttachFunctions(sqlite3 *); void sqlite3AttachFunctions(sqlite3 *);
void sqlite3MinimumFileFormat(Parse*, int, int); void sqlite3MinimumFileFormat(Parse*, int, int);
void sqlite3SchemaFree(void *);
DbSchema *sqlite3SchemaGet(Btree *);
int sqlite3SchemaToIndex(sqlite3 *db, DbSchema *);
void sqlite3MallocClearFailed(); void sqlite3MallocClearFailed();
#ifdef NDEBUG #ifdef NDEBUG

View File

@@ -58,6 +58,7 @@ void sqlite3BeginTrigger(
int iDb; /* The database to store the trigger in */ int iDb; /* The database to store the trigger in */
Token *pName; /* The unqualified db name */ Token *pName; /* The unqualified db name */
DbFixer sFix; DbFixer sFix;
int iTabDb;
if( isTemp ){ if( isTemp ){
/* If TEMP was specified, then the trigger name may not be qualified. */ /* 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; if( !pTableName || sqlite3Tsd()->mallocFailed ) goto trigger_cleanup;
pTab = sqlite3SrcListLookup(pParse, pTableName); 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; iDb = 1;
} }
@@ -105,7 +106,7 @@ void sqlite3BeginTrigger(
if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto trigger_cleanup; 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); sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
goto trigger_cleanup; goto trigger_cleanup;
} }
@@ -130,17 +131,18 @@ void sqlite3BeginTrigger(
" trigger on table: %S", pTableName, 0); " trigger on table: %S", pTableName, 0);
goto trigger_cleanup; goto trigger_cleanup;
} }
iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
#ifndef SQLITE_OMIT_AUTHORIZATION #ifndef SQLITE_OMIT_AUTHORIZATION
{ {
int code = SQLITE_CREATE_TRIGGER; 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; 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) ){ if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){
goto trigger_cleanup; 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; goto trigger_cleanup;
} }
} }
@@ -161,8 +163,8 @@ void sqlite3BeginTrigger(
pTrigger->name = zName; pTrigger->name = zName;
zName = 0; zName = 0;
pTrigger->table = sqliteStrDup(pTableName->a[0].zName); pTrigger->table = sqliteStrDup(pTableName->a[0].zName);
pTrigger->iDb = iDb; pTrigger->pSchema = db->aDb[iDb].pSchema;
pTrigger->iTabDb = pTab->iDb; pTrigger->iTabDb = iTabDb;
pTrigger->op = op; pTrigger->op = op;
pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
pTrigger->pWhen = sqlite3ExprDup(pWhen); pTrigger->pWhen = sqlite3ExprDup(pWhen);
@@ -196,16 +198,18 @@ void sqlite3FinishTrigger(
Trigger *pTrig = 0; /* The trigger whose construction is finishing up */ Trigger *pTrig = 0; /* The trigger whose construction is finishing up */
sqlite3 *db = pParse->db; /* The database */ sqlite3 *db = pParse->db; /* The database */
DbFixer sFix; DbFixer sFix;
int iDb; /* Database containing the trigger */
pTrig = pParse->pNewTrigger; pTrig = pParse->pNewTrigger;
pParse->pNewTrigger = 0; pParse->pNewTrigger = 0;
if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup; if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup;
iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
pTrig->step_list = pStepList; pTrig->step_list = pStepList;
while( pStepList ){ while( pStepList ){
pStepList->pTrig = pTrig; pStepList->pTrig = pTrig;
pStepList = pStepList->pNext; 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) ){ && sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){
goto triggerfinish_cleanup; goto triggerfinish_cleanup;
} }
@@ -232,22 +236,22 @@ void sqlite3FinishTrigger(
/* Make an entry in the sqlite_master table */ /* Make an entry in the sqlite_master table */
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
if( v==0 ) goto triggerfinish_cleanup; if( v==0 ) goto triggerfinish_cleanup;
sqlite3BeginWriteOperation(pParse, 0, pTrig->iDb); sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3OpenMasterTable(v, pTrig->iDb); sqlite3OpenMasterTable(v, iDb);
addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig); addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
sqlite3VdbeChangeP3(v, addr+2, pTrig->name, 0); sqlite3VdbeChangeP3(v, addr+2, pTrig->name, 0);
sqlite3VdbeChangeP3(v, addr+3, pTrig->table, 0); sqlite3VdbeChangeP3(v, addr+3, pTrig->table, 0);
sqlite3VdbeChangeP3(v, addr+6, (char*)pAll->z, pAll->n); sqlite3VdbeChangeP3(v, addr+6, (char*)pAll->z, pAll->n);
sqlite3ChangeCookie(db, v, pTrig->iDb); sqlite3ChangeCookie(db, v, iDb);
sqlite3VdbeAddOp(v, OP_Close, 0, 0); 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); sqlite3MPrintf("type='trigger' AND name='%q'", pTrig->name), P3_DYNAMIC);
} }
if( db->init.busy ){ if( db->init.busy ){
Table *pTab; Table *pTab;
Trigger *pDel; Trigger *pDel;
pDel = sqlite3HashInsert(&db->aDb[pTrig->iDb].trigHash, pDel = sqlite3HashInsert(&db->aDb[iDb].pSchema->trigHash,
pTrig->name, strlen(pTrig->name)+1, pTrig); pTrig->name, strlen(pTrig->name)+1, pTrig);
if( pDel ){ if( pDel ){
assert( sqlite3Tsd()->mallocFailed && pDel==pTrig ); assert( sqlite3Tsd()->mallocFailed && pDel==pTrig );
@@ -445,7 +449,7 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName){
for(i=OMIT_TEMPDB; i<db->nDb; i++){ for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue; 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 ) break;
} }
if( !pTrigger ){ if( !pTrigger ){
@@ -478,11 +482,11 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
sqlite3 *db = pParse->db; sqlite3 *db = pParse->db;
int iDb; int iDb;
iDb = pTrigger->iDb; iDb = sqlite3SchemaToIndex(pParse->db, pTrigger->pSchema);
assert( iDb>=0 && iDb<db->nDb ); assert( iDb>=0 && iDb<db->nDb );
pTable = tableOfTrigger(db, pTrigger); pTable = tableOfTrigger(db, pTrigger);
assert(pTable); assert(pTable);
assert( pTable->iDb==iDb || iDb==1 ); assert( pTable->pSchema==pTrigger->pSchema || iDb==1 );
#ifndef SQLITE_OMIT_AUTHORIZATION #ifndef SQLITE_OMIT_AUTHORIZATION
{ {
int code = SQLITE_DROP_TRIGGER; 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){ void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
Trigger *pTrigger; Trigger *pTrigger;
int nName = strlen(zName); 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 ){ if( pTrigger ){
Table *pTable = tableOfTrigger(db, pTrigger); Table *pTable = tableOfTrigger(db, pTrigger);
assert( pTable!=0 ); assert( pTable!=0 );
@@ -620,7 +624,7 @@ static SrcList *targetSrcList(
int iDb; /* Index of the database to use */ int iDb; /* Index of the database to use */
SrcList *pSrc; /* SrcList to be returned */ SrcList *pSrc; /* SrcList to be returned */
iDb = pStep->pTrig->iDb; iDb = sqlite3SchemaToIndex(pParse->db, pStep->pTrig->pSchema);
if( iDb==0 || iDb>=2 ){ if( iDb==0 || iDb>=2 ){
assert( iDb<pParse->db->nDb ); assert( iDb<pParse->db->nDb );
sDb.z = (u8*)pParse->db->aDb[iDb].zName; sDb.z = (u8*)pParse->db->aDb[iDb].zName;

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle UPDATE statements. ** 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" #include "sqliteInt.h"
@@ -89,6 +89,7 @@ void sqlite3Update(
int openAll = 0; /* True if all indices need to be opened */ int openAll = 0; /* True if all indices need to be opened */
AuthContext sContext; /* The authorization context */ AuthContext sContext; /* The authorization context */
NameContext sNC; /* The name-context to resolve expressions in */ NameContext sNC; /* The name-context to resolve expressions in */
int iDb; /* Database containing the table being updated */
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
int isView; /* Trying to update a view */ int isView; /* Trying to update a view */
@@ -107,6 +108,7 @@ void sqlite3Update(
*/ */
pTab = sqlite3SrcListLookup(pParse, pTabList); pTab = sqlite3SrcListLookup(pParse, pTabList);
if( pTab==0 ) goto update_cleanup; if( pTab==0 ) goto update_cleanup;
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
/* Figure out if we have any triggers and if the table being /* Figure out if we have any triggers and if the table being
** updated is a view ** updated is a view
@@ -192,7 +194,7 @@ void sqlite3Update(
{ {
int rc; int rc;
rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, 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 ){ if( rc==SQLITE_DENY ){
goto update_cleanup; goto update_cleanup;
}else if( rc==SQLITE_IGNORE ){ }else if( rc==SQLITE_IGNORE ){
@@ -257,7 +259,7 @@ void sqlite3Update(
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
if( v==0 ) goto update_cleanup; if( v==0 ) goto update_cleanup;
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); 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 /* If we are trying to update a view, realize that view into
** a ephemeral table. ** a ephemeral table.
@@ -307,7 +309,7 @@ void sqlite3Update(
/* Open a cursor and make it point to the record that is /* Open a cursor and make it point to the record that is
** being updated. ** being updated.
*/ */
sqlite3OpenTableForReading(v, iCur, pTab); sqlite3OpenTableForReading(v, iCur, iDb, pTab);
} }
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
@@ -362,7 +364,7 @@ void sqlite3Update(
** action, then we need to open all indices because we might need ** action, then we need to open all indices because we might need
** to be deleting some records. ** 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_OpenWrite, iCur, pTab->tnum);
sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol); sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
if( onError==OE_Replace ){ if( onError==OE_Replace ){
@@ -378,7 +380,7 @@ void sqlite3Update(
} }
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
if( openAll || aIdxUsed[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, sqlite3VdbeOp3(v, OP_OpenWrite, iCur+i+1, pIdx->tnum,
(char*)&pIdx->keyInfo, P3_KEYINFO); (char*)&pIdx->keyInfo, P3_KEYINFO);
assert( pParse->nTab>iCur+i+1 ); assert( pParse->nTab>iCur+i+1 );

View File

@@ -43,7 +43,7 @@
** in this file for details. If in doubt, do not deviate from existing ** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code. ** 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 "sqliteInt.h"
#include "os.h" #include "os.h"
@@ -2422,11 +2422,11 @@ case OP_SetCookie: { /* no-push */
rc = sqlite3BtreeUpdateMeta(pDb->pBt, 1+pOp->p2, (int)pTos->i); rc = sqlite3BtreeUpdateMeta(pDb->pBt, 1+pOp->p2, (int)pTos->i);
if( pOp->p2==0 ){ if( pOp->p2==0 ){
/* When the schema cookie changes, record the new cookie internally */ /* 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; db->flags |= SQLITE_InternChanges;
}else if( pOp->p2==1 ){ }else if( pOp->p2==1 ){
/* Record changes in the file format */ /* Record changes in the file format */
pDb->file_format = pTos->i; pDb->pSchema->file_format = pTos->i;
} }
assert( (pTos->flags & MEM_Dyn)==0 ); assert( (pTos->flags & MEM_Dyn)==0 );
pTos--; pTos--;
@@ -2530,8 +2530,8 @@ case OP_OpenWrite: { /* no-push */
assert( pX!=0 ); assert( pX!=0 );
if( pOp->opcode==OP_OpenWrite ){ if( pOp->opcode==OP_OpenWrite ){
wrFlag = 1; wrFlag = 1;
if( pDb->file_format < p->minWriteFileFormat ){ if( pDb->pSchema->file_format < p->minWriteFileFormat ){
p->minWriteFileFormat = pDb->file_format; p->minWriteFileFormat = pDb->pSchema->file_format;
} }
}else{ }else{
wrFlag = 0; wrFlag = 0;

View File

@@ -16,7 +16,7 @@
** so is applicable. Because this module is responsible for selecting ** so is applicable. Because this module is responsible for selecting
** indices, you might also think of this module as the "query optimizer". ** 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" #include "sqliteInt.h"
@@ -1554,8 +1554,9 @@ WhereInfo *sqlite3WhereBegin(
sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
pLevel = pWInfo->a; pLevel = pWInfo->a;
for(i=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){ for(i=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){
Table *pTab; Table *pTab; /* Table to open */
Index *pIx; Index *pIx; /* Index used to access pTab (if any) */
int iDb; /* Index of database containing table/index */
int iIdxCur = pLevel->iIdxCur; int iIdxCur = pLevel->iIdxCur;
#ifndef SQLITE_OMIT_EXPLAIN #ifndef SQLITE_OMIT_EXPLAIN
@@ -1576,13 +1577,15 @@ WhereInfo *sqlite3WhereBegin(
#endif /* SQLITE_OMIT_EXPLAIN */ #endif /* SQLITE_OMIT_EXPLAIN */
pTabItem = &pTabList->a[pLevel->iFrom]; pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab; pTab = pTabItem->pTab;
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
if( pTab->isTransient || pTab->pSelect ) continue; if( pTab->isTransient || pTab->pSelect ) continue;
if( (pLevel->flags & WHERE_IDX_ONLY)==0 ){ if( (pLevel->flags & WHERE_IDX_ONLY)==0 ){
sqlite3OpenTableForReading(v, pTabItem->iCursor, pTab); sqlite3OpenTableForReading(v, pTabItem->iCursor, iDb, pTab);
} }
pLevel->iTabCur = pTabItem->iCursor; pLevel->iTabCur = pTabItem->iCursor;
if( (pIx = pLevel->pIdx)!=0 ){ 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)); VdbeComment((v, "# %s", pIx->zName));
sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIx->tnum, sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIx->tnum,
(char*)&pIx->keyInfo, P3_KEYINFO); (char*)&pIx->keyInfo, P3_KEYINFO);
@@ -1590,7 +1593,7 @@ WhereInfo *sqlite3WhereBegin(
if( (pLevel->flags & WHERE_IDX_ONLY)!=0 ){ if( (pLevel->flags & WHERE_IDX_ONLY)!=0 ){
sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, pIx->nColumn+1); sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, pIx->nColumn+1);
} }
sqlite3CodeVerifySchema(pParse, pTab->iDb); sqlite3CodeVerifySchema(pParse, iDb);
} }
pWInfo->iTop = sqlite3VdbeCurrentAddr(v); pWInfo->iTop = sqlite3VdbeCurrentAddr(v);

View File

@@ -10,7 +10,7 @@
#*********************************************************************** #***********************************************************************
# This file runs all tests. # 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] set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
@@ -56,6 +56,11 @@ set EXCLUDE {
malloc.test malloc.test
misuse.test misuse.test
memleak.test memleak.test
malloc2.test
malloc3.test
malloc4.test
malloc5.test
} }
# Test files btree2.test and btree4.test don't work if the # 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} catch {source $testdir/misuse.test}
set sqlite_open_file_count 0 set sqlite_open_file_count 0
catch {source $testdir/malloc.test} # catch {source $testdir/malloc.test}
catch {db close} catch {db close}
set sqlite_open_file_count 0 set sqlite_open_file_count 0

View File

@@ -29,7 +29,7 @@
# The solution to the problem was to detect that the table is locked # The solution to the problem was to detect that the table is locked
# before the index entry is deleted. # 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] 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. # Try to delete a row from the table. The delete should fail.
# #
breakpoint
do_test delete2-1.6 { do_test delete2-1.6 {
catchsql { catchsql {
DELETE FROM q WHERE rowid=1 DELETE FROM q WHERE rowid=1

View File

@@ -11,9 +11,7 @@
# This file implements regression tests for SQLite library. The # This file implements regression tests for SQLite library. The
# focus of this file is testing the SELECT statement. # focus of this file is testing the SELECT statement.
# #
# $Id: shared.test,v 1.1 2005/12/30 16:28:02 danielk1977 Exp $ # $Id: shared.test,v 1.2 2006/01/05 11:34:34 danielk1977 Exp $
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
@@ -23,6 +21,7 @@ ifcapable !shared_cache {
finish_test finish_test
return return
} }
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
# Test organization: # Test organization:
# #
@@ -31,6 +30,7 @@ ifcapable !shared_cache {
# shared-2.*: Test that a read transaction can co-exist with a # shared-2.*: Test that a read transaction can co-exist with a
# write-transaction, including a simple test to ensure the # write-transaction, including a simple test to ensure the
# external locking protocol is still working. # external locking protocol is still working.
# shared-3.*: Simple test of read-uncommitted mode.
# #
do_test shared-1.1 { do_test shared-1.1 {
@@ -73,62 +73,37 @@ do_test shared-1.4 {
} db2 } db2
} {1 {database is locked}} } {1 {database is locked}}
do_test shared-1.5 { do_test shared-1.5 {
# Using connection 2 (the one without the open transaction), create a # Using connection 2 (the one without the open transaction), try to create
# new table and add a row to it. This is permitted as the transaction # a new table. This should fail because of the open read transaction
# started by connection 1 is currently a 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 { execsql {
CREATE TABLE def(d, e, f); 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'); INSERT INTO def VALUES('IV', 'V', 'VI');
} }
} {} } {}
do_test shared-1.7 { do_test shared-1.7 {
# Read from the sqlite_master table with connection 1 (inside the # Read from the sqlite_master table with connection 1 (inside the
# transaction). Then test that we can no longer create a table # transaction). Then test that we can not do this with connection 2. This
# with connection 2. This is because of the read-lock on sqlite_master. # is because of the schema-modified lock established by connection 1
# in the previous test case.
execsql { execsql {
SELECT * FROM sqlite_master; 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 { catchsql {
SELECT * FROM sqlite_master; SELECT * FROM sqlite_master;
} db2 } db2
} {1 {database is locked}} } {1 {database is locked}}
do_test shared-1.9 { do_test shared-1.8 {
# Commit the connection 1 transaction. # Commit the connection 1 transaction.
execsql { execsql {
COMMIT; COMMIT;
@@ -180,7 +155,7 @@ do_test shared-2.3 {
] [ ] [
catchsql { SELECT * FROM def; } db2 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 { do_test shared-2.4 {
# Commit the open transaction on db. db2 still holds a read-transaction. # Commit the open transaction on db. db2 still holds a read-transaction.
# This should prevent db3 from writing to the database, but not from # 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 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 {db close}
catch {db2 close} catch {db2 close}

View File

@@ -11,7 +11,7 @@
# This file implements some common TCL routines used for regression # This file implements some common TCL routines used for regression
# testing the SQLite library # 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 # Make sure tclsqlite3 was compiled correctly. Abort now with an
# error message if not. # error message if not.
@@ -449,7 +449,7 @@ proc check_for_leaks {} {
# The first command in this block will probably fail on windows. This # The first command in this block will probably fail on windows. This
# means there will be no stack dump available. # means there will be no stack dump available.
if {$cnt < 25} { if {$cnt < 25 && $backtrace!=""} {
catch { catch {
set stuff [eval "exec addr2line -e ./testfixture -f $backtrace"] set stuff [eval "exec addr2line -e ./testfixture -f $backtrace"]
foreach {func line} $stuff { foreach {func line} $stuff {

223
www/sharedcache.tcl Normal file
View 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 = &lt;boolean&gt;;
/* Retrieve the current value of the read-uncommitted flag */
PRAGMA read_uncommitted;
</pre>
}
HEADING 2 {Schema (sqlite_master) Level Locking}
puts {
<p>The <i>sqlite_master</i> table supports shared-cache read and write
locks in the same way as all other database tables (see description
above). The following special rules also apply:
</p>
<ul>
<li>A connection must obtain a read-lock on <i>sqlite_master</i> before
accessing any database tables or obtaining any other read or write locks.</li>
<li>Before executing a statement that modifies the database schema (i.e.
a CREATE or DROP TABLE statement), a connection must obtain a write-lock on
<i>sqlite_master</i>.
</li>
<li>A connection may not compile an SQL statement that refers to database
tables if any other connection is holding a write-lock on <i>sqlite_master</i>.
</li>
</ul>
}
HEADING 1 {Thread Related Issues}
puts {
<p>When shared-cache mode is enabled, a database connection may only be
used by the thread that called sqlite3_open() to create it. If another
thread attempts to use the database connection, in most cases an
SQLITE_MISUSE error is returned. However this is not guaranteed and
programs should not depend on this behaviour, in some cases a segfault
may result.
</p>
}
HEADING 1 {Enabling Shared-Cache Mode}
puts {
<p>Shared-cache mode is enabled on a thread-wide basis. Using the C
interface, the following API can be used to enable or disable shared-cache
mode for the calling thread:
</p>
<pre>
int sqlite3_enable_shared_cache(int);
</pre>
<p>It is illegal to call sqlite3_enable_shared_cache() if one or more
open database connections were opened by the calling thread. If the argument
is non-zero, shared-cache mode is enabled. If the argument is zero,
shared-cache mode is disabled. The return value is either SQLITE_OK (if the
operation was successful), SQLITE_NOMEM (if a malloc() failed), or
SQLITE_MISUSE (if the thread has open database connections).
</p>
}
footer $rcsid