diff --git a/VERSION b/VERSION
index 73462a5a13..f225a78adf 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.5.1
+2.5.2
diff --git a/manifest b/manifest
index 1c509bdb70..98a3774ad3 100644
--- a/manifest
+++ b/manifest
@@ -1,9 +1,9 @@
-C Partial\sfix\sfor\sa\sproblem\swith\sLEFT\sOUTER\sJOIN.\s\sIt\sused\sto\sbe\sthat\sthe\stest\nfor\sthe\sright-hand\stable\snot\smatching\sthe\sleft\stable\soccurred\safter\sall\nON,\sUSING,\sWHERE\sclause\sprocessing.\s\sThe\stest\sshould\soccur\safter\sON\sand\nUSING\sclauses\sare\schecked\sbut\sbefore\sthe\sWHERE\sclause\sis\scheck.\s\sThis\sfix\nworks\sas\slong\sas\sthe\stotal\snumber\sof\s"AND"\sseparated\sterms\sin\sthe\sON,\sUSING,\nand\sWHERE\sclause\sdoes\snot\sexceed\s32.\s\sTo\sdo:\smake\sthis\swork\sfor\sany\snumber\nof\sterms\sand\sadd\stest\scases.\nthat\s(CVS\s639)
-D 2002-06-24T22:01:58
+C Added\ssupport\sfor\sthe\s"sqlite_temp_master"\stable.\s\sIncreased\sthe\sversion\nnumber\sto\s2.5.2.\s(CVS\s640)
+D 2002-06-25T01:09:11
F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c
F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
-F VERSION 9a1a4bc4ca9e44b3ccf4c764cb670aae41b078a0
+F VERSION 2ca20d4461e9496d4ae27191e7273a12369ff17c
F aclocal.m4 11faa843caa38fd451bc6aeb43e248d1723a269d
F config.guess f38b1e93d1e0fa6f5a6913e9e7b12774b9232588
F config.sub f14b07d544ca26b5d698259045136b783e18fc7f
@@ -20,7 +20,7 @@ F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
F src/btree.c 6aaa67d7eab70c2531dc13e5d9eb87e626c0b4d7
F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
-F src/build.c 95eac6ce4ae2871388d49066c78dd0657ce40a1f
+F src/build.c 846eb3ee0e160e691766108a7136e196f8f8231b
F src/delete.c 44c45460b1e03033756e35adc6d569ffbf30b725
F src/encode.c 346b12b46148506c32038524b95c4631ab46d760
F src/expr.c cb50a72c491954d58be2f182366e45a1e252bf2e
@@ -28,20 +28,20 @@ F src/func.c 5eae8227a8b0d276a64d51a3880a6e86f238fedf
F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
F src/insert.c 4bb40ed9dbaba4516fc2abbcff3f08d5687b073c
-F src/main.c 0e922ecfe4ce58c3e5c49f111d86003607d2114b
+F src/main.c 43d5f4e38108129a13cf42c59087e6e20b3596ad
F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b
F src/os.c 9cc40c5384baba4a85e160e67807645ca98ba3cc
F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10
F src/pager.c 1e41053c949cea1f09d8dafada5fe8f90785e650
F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e
-F src/parse.y 2285d8967d7334d52a2188089e5a881d73ba56f6
+F src/parse.y c75ea2580de675bcb80ff8b7c10c0a15e02a21ab
F src/printf.c 236ed7a79386feed4456fa728fff8be793f1547c
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
F src/select.c f7d74f20f5ecc335fbccba367eda727b9d6fb299
-F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15
+F src/shell.c 7b9d98ef3976ff5e44c18620dd17d32af83fbdd6
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in 7c8882e352cb70818cfaf9bdb5b1b3bee81ef144
-F src/sqliteInt.h d3c1448890ba65e6be381b50b7e7ce7fca142322
+F src/sqliteInt.h 3d1d86cb9ea4f06e49af855267478e3661abcd1b
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1
F src/test1.c 5cc4f0bbf38237e04e1b2077e285b41bfb4c4cbf
@@ -49,12 +49,12 @@ F src/test2.c 669cc22781c6461a273416ec1a7414d25c081730
F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
F src/tokenize.c ac4c46f190346b87da54ec3e2605d160af80c619
-F src/trigger.c 21342af6ac031fece39c8fc6eabd1739ca5327c1
+F src/trigger.c d88ab4d68d68955c217b38fb6717e090fbbf54a4
F src/update.c 6f6a4dcd71cd9ff730b7f12c83de5498cde4924f
F src/util.c 876b259f9186e84b944b72e793dd3dad50e63e95
-F src/vdbe.c 774f79483ce809b27c3bdb02afd7295cc3c7acd4
+F src/vdbe.c 0b1ad7c3cbc638d1f73725bbc4e667c3ee8f7081
F src/vdbe.h a9292f2b5fcecef924fa255fb74609e9cbc776c2
-F src/where.c 259d7fb77191b13718c271926b7c14afbbe7346b
+F src/where.c 913fa33977c8dddfc259d9b2c38504b475738c43
F test/all.test e4d3821eeba751829b419cd47814bd20af4286d1
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1
@@ -98,12 +98,12 @@ F test/subselect.test f3bc1dcbddddcea08d818fcff75228ad3464fc83
F test/table.test 42511f98a3e9bbee62913e3ae1774777faa23d35
F test/tableapi.test 3c80421a889e1d106df16e5800fa787f0d2914a6
F test/tclsqlite.test 79deeffd7cd637ca0f06c5dbbf2f44d272079533
-F test/temptable.test ae58694c0fdd2d0b781508b64adc5aee3416aeed
+F test/temptable.test 9ed7ec0288f887e132de66d90c428ad109105f67
F test/tester.tcl dc1b56bd628b487e4d75bfd1e7480b5ed8810ac6
F test/trans.test ae0b9a82d5d34122c3a3108781eb8d078091ccee
F test/trigger1.test bb63749fa8a395a60541100607d86381604b7194
F test/trigger2.test c12759a0d7ba6488d9d24c96a1352ddee995c1ab
-F test/trigger3.test b4aca721ba92956c7fa16bb0158254f3c1b73efa
+F test/trigger3.test 7dfe798d7e72c13720394685fe353112e3f31adf
F test/unique.test 572aa791327c1e8d797932263e9d67f176cfdb44
F test/update.test a0aa0bf83e6fad8407d0e4ad25ebb09b513f5bf4
F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe
@@ -122,22 +122,22 @@ F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf
F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4
F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b
F www/c_interface.tcl 58cf4d128dcae08d91d0011c6d4d11de323f470f
-F www/changes.tcl 31a8fec4f078a60b7de5f19859297f375e9ec8da
+F www/changes.tcl 08de0b1b50d3651ac3bd6b0d44c9ebe0072b55b3
F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2
F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060
F www/download.tcl 29aa6679ca29621d10613f60ebbbda18f4b91c49
F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
-F www/faq.tcl 45bdb18b75ac3aa1befec42985fb892413aac0bb
+F www/faq.tcl 06e95342a101fde3ce59dbf128233a178502587e
F www/formatchng.tcl f604cde78f1ac9c29420136109b04407408e876e
F www/index.tcl d0c52fbf031d0a3ee6d9d77aa669d5a4b24b6130
-F www/lang.tcl cf22bf18dbd6bec3b7d0b00ad998dd1f88193ea2
+F www/lang.tcl 8c3d0bda030f110c754b5edbad75eddf5dbe2ed1
F www/mingw.tcl f1c7c0a7f53387dd9bb4f8c7e8571b7561510ebc
F www/opcode.tcl bdec8ef9f100dbd87bbef8976c54b88e43fd8ccc
F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f
-F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
+F www/sqlite.tcl ac64065d0c5e2de0f71238d55b2c14bb5c5c194c
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P d861489e1f7dffd1105c271fe8597f73e5b1703c
-R d718b83d86c9331bc69c93f673a44be9
+P 8b6574cfa86daaae910f8f3ee3c4723a21fb9e53
+R c96fc24249eaf46c6e1d5e0a79495140
U drh
-Z b9b5d0f63e47642b79ff89410dec8a4b
+Z 421a17002ac6e743396eaf770c048af4
diff --git a/manifest.uuid b/manifest.uuid
index 9fc9568e12..69eb0f6e91 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-8b6574cfa86daaae910f8f3ee3c4723a21fb9e53
\ No newline at end of file
+9c1432bf7485258e485bd652e3acdaeabbfe8850
\ No newline at end of file
diff --git a/src/build.c b/src/build.c
index 7e7920278e..3d4909741e 100644
--- a/src/build.c
+++ b/src/build.c
@@ -25,11 +25,29 @@
** ROLLBACK
** PRAGMA
**
-** $Id: build.c,v 1.97 2002/06/20 11:36:49 drh Exp $
+** $Id: build.c,v 1.98 2002/06/25 01:09:11 drh Exp $
*/
#include "sqliteInt.h"
#include
+/*
+** This routine is called when a new SQL statement is beginning to
+** be parsed. Check to see if the schema for the database needs
+** to be read from the SQLITE_MASTER and SQLITE_TEMP_MASTER tables.
+** If it does, then read it.
+*/
+void sqliteBeginParse(Parse *pParse, int explainFlag){
+ sqlite *db = pParse->db;
+ pParse->explain = explainFlag;
+ if((db->flags & SQLITE_Initialized)==0 && pParse->initFlag==0 ){
+ int rc = sqliteInit(db, &pParse->zErrMsg);
+ if( rc!=SQLITE_OK ){
+ pParse->rc = rc;
+ pParse->nErr++;
+ }
+ }
+}
+
/*
** This routine is called after a single SQL statement has been
** parsed and we want to execute the VDBE code to implement
@@ -48,6 +66,7 @@ void sqliteExec(Parse *pParse){
if( pParse->explain ){
rc = sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg,
&pParse->zErrMsg);
+ db->next_cookie = db->schema_cookie;
}else{
FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
sqliteVdbeTrace(pParse->pVdbe, trace);
@@ -70,7 +89,8 @@ void sqliteExec(Parse *pParse){
** of that table. Return NULL if not found.
*/
Table *sqliteFindTable(sqlite *db, const char *zName){
- Table *p = sqliteHashFind(&db->tblHash, zName, strlen(zName)+1);
+ Table *p;
+ p = sqliteHashFind(&db->tblHash, zName, strlen(zName)+1);
return p;
}
@@ -80,7 +100,8 @@ Table *sqliteFindTable(sqlite *db, const char *zName){
** Return NULL if not found.
*/
Index *sqliteFindIndex(sqlite *db, const char *zName){
- Index *p = sqliteHashFind(&db->idxHash, zName, strlen(zName)+1);
+ Index *p;
+ p = sqliteHashFind(&db->idxHash, zName, strlen(zName)+1);
return p;
}
@@ -99,7 +120,6 @@ static void sqliteDeleteIndex(sqlite *db, Index *p){
if( pOld!=0 && pOld!=p ){
sqliteHashInsert(&db->idxHash, pOld->zName, strlen(pOld->zName)+1, pOld);
}
- sqliteHashInsert(&db->idxDrop, p, 0, 0);
sqliteFree(p);
}
@@ -122,26 +142,51 @@ void sqliteUnlinkAndDeleteIndex(sqlite *db, Index *pIndex){
}
/*
-** Move the given index to the pending DROP INDEX queue if it has
-** been committed. If this index was never committed, then just
-** delete it.
-**
-** Indices on the pending drop queue are deleted when a COMMIT is
-** executed. If a ROLLBACK occurs, the indices are moved back into
-** the main index hash table.
+** Erase all schema information from the in-memory hash tables of
+** database connection. This routine is called to reclaim memory
+** before the connection closes. It is also called during a rollback
+** if there were schema changes during the transaction.
*/
-static void sqlitePendingDropIndex(sqlite *db, Index *p){
- if( !p->isCommit ){
- sqliteUnlinkAndDeleteIndex(db, p);
- }else{
- Index *pOld;
- pOld = sqliteHashInsert(&db->idxHash, p->zName, strlen(p->zName)+1, 0);
- if( pOld!=0 && pOld!=p ){
- sqliteHashInsert(&db->idxHash, pOld->zName, strlen(pOld->zName)+1, pOld);
- }
- sqliteHashInsert(&db->idxDrop, p, 0, p);
- p->isDropped = 1;
+void sqliteResetInternalSchema(sqlite *db){
+ HashElem *pElem;
+ Hash temp1;
+ Hash temp2;
+
+ temp1 = db->tblHash;
+ temp2 = db->trigHash;
+ sqliteHashInit(&db->trigHash, SQLITE_HASH_STRING, 0);
+ sqliteHashClear(&db->idxHash);
+ for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
+ Trigger *pTrigger = sqliteHashData(pElem);
+ sqliteDeleteTrigger(pTrigger);
}
+ sqliteHashClear(&temp2);
+ sqliteHashInit(&db->tblHash, SQLITE_HASH_STRING, 0);
+ for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
+ Table *pTab = sqliteHashData(pElem);
+ sqliteDeleteTable(db, pTab);
+ }
+ sqliteHashClear(&temp1);
+ db->flags &= ~(SQLITE_Initialized|SQLITE_InternChanges);
+}
+
+/*
+** This routine is called whenever a rollback occurs. If there were
+** schema changes during the transaction, then we have to reset the
+** internal hash tables and reload them from disk.
+*/
+void sqliteRollbackInternalChanges(sqlite *db){
+ if( db->flags & SQLITE_InternChanges ){
+ sqliteResetInternalSchema(db);
+ }
+}
+
+/*
+** This routine is called when a commit occurs.
+*/
+void sqliteCommitInternalChanges(sqlite *db){
+ db->schema_cookie = db->next_cookie;
+ db->flags &= ~SQLITE_InternChanges;
}
/*
@@ -185,185 +230,9 @@ static void sqliteUnlinkAndDeleteTable(sqlite *db, Table *p){
assert( db!=0 );
pOld = sqliteHashInsert(&db->tblHash, p->zName, strlen(p->zName)+1, 0);
assert( pOld==0 || pOld==p );
- sqliteHashInsert(&db->tblDrop, p, 0, 0);
sqliteDeleteTable(db, p);
}
-/*
-** Move the given table to the pending DROP TABLE queue if it has
-** been committed. If this table was never committed, then just
-** delete it. Do the same for all its indices.
-**
-** Table on the drop queue are not actually deleted until a COMMIT
-** statement is executed. If a ROLLBACK occurs instead of a COMMIT,
-** then the tables on the drop queue are moved back into the main
-** hash table.
-*/
-static void sqlitePendingDropTable(sqlite *db, Table *pTbl){
- if( !pTbl->isCommit ){
- sqliteUnlinkAndDeleteTable(db, pTbl);
- }else{
- Table *pOld;
- Index *pIndex, *pNext;
- pOld = sqliteHashInsert(&db->tblHash, pTbl->zName, strlen(pTbl->zName)+1,0);
- assert( pOld==pTbl );
- sqliteHashInsert(&db->tblDrop, pTbl, 0, pTbl);
- for(pIndex = pTbl->pIndex; pIndex; pIndex=pNext){
- pNext = pIndex->pNext;
- sqlitePendingDropIndex(db, pIndex);
- }
- }
-}
-
-/*
-** Check all Tables and Indexes in the internal hash table and commit
-** any additions or deletions to those hash tables.
-**
-** When executing CREATE TABLE and CREATE INDEX statements, the Table
-** and Index structures are created and added to the hash tables, but
-** the "isCommit" field is not set. This routine sets those fields.
-** When executing DROP TABLE and DROP INDEX, the table or index structures
-** are moved out of tblHash and idxHash into tblDrop and idxDrop. This
-** routine deletes the structure in tblDrop and idxDrop.
-**
-** See also: sqliteRollbackInternalChanges()
-*/
-void sqliteCommitInternalChanges(sqlite *db){
- HashElem *pElem;
- if( (db->flags & SQLITE_InternChanges)==0 ) return;
- db->schema_cookie = db->next_cookie;
- for(pElem=sqliteHashFirst(&db->tblHash); pElem; pElem=sqliteHashNext(pElem)){
- Table *pTable = sqliteHashData(pElem);
- pTable->isCommit = 1;
- }
- for(pElem=sqliteHashFirst(&db->tblDrop); pElem; pElem=sqliteHashNext(pElem)){
- Table *pTable = sqliteHashData(pElem);
- sqliteDeleteTable(db, pTable);
- }
- sqliteHashClear(&db->tblDrop);
- for(pElem=sqliteHashFirst(&db->idxHash); pElem; pElem=sqliteHashNext(pElem)){
- Index *pIndex = sqliteHashData(pElem);
- pIndex->isCommit = 1;
- }
- while( (pElem=sqliteHashFirst(&db->idxDrop))!=0 ){
- Index *pIndex = sqliteHashData(pElem);
- sqliteUnlinkAndDeleteIndex(db, pIndex);
- }
- sqliteHashClear(&db->idxDrop);
-
- /* Set the commit flag on all triggers added this transaction */
- for(pElem=sqliteHashFirst(&db->trigHash); pElem; pElem=sqliteHashNext(pElem)){
- Trigger *pTrigger = sqliteHashData(pElem);
- pTrigger->isCommit = 1;
- }
-
- /* Delete the structures for triggers removed this transaction */
- pElem = sqliteHashFirst(&db->trigDrop);
- while( pElem ){
- Trigger *pTrigger = sqliteHashData(pElem);
- sqliteDeleteTrigger(pTrigger);
- pElem = sqliteHashNext(pElem);
- }
- sqliteHashClear(&db->trigDrop);
-
- db->flags &= ~SQLITE_InternChanges;
-}
-
-/*
-** This routine runs when one or more CREATE TABLE, CREATE INDEX,
-** DROP TABLE, or DROP INDEX statements gets rolled back. The
-** additions or deletions of Table and Index structures in the
-** internal hash tables are undone.
-**
-** See also: sqliteCommitInternalChanges()
-*/
-void sqliteRollbackInternalChanges(sqlite *db){
- Hash toDelete;
- HashElem *pElem;
- if( (db->flags & SQLITE_InternChanges)==0 ) return;
- sqliteHashInit(&toDelete, SQLITE_HASH_POINTER, 0);
- db->next_cookie = db->schema_cookie;
- for(pElem=sqliteHashFirst(&db->tblHash); pElem; pElem=sqliteHashNext(pElem)){
- Table *pTable = sqliteHashData(pElem);
- if( !pTable->isCommit ){
- sqliteHashInsert(&toDelete, pTable, 0, pTable);
- }
- }
- for(pElem=sqliteHashFirst(&toDelete); pElem; pElem=sqliteHashNext(pElem)){
- Table *pTable = sqliteHashData(pElem);
- sqliteUnlinkAndDeleteTable(db, pTable);
- }
- sqliteHashClear(&toDelete);
- for(pElem=sqliteHashFirst(&db->tblDrop); pElem; pElem=sqliteHashNext(pElem)){
- Table *pOld, *p = sqliteHashData(pElem);
- assert( p->isCommit );
- pOld = sqliteHashInsert(&db->tblHash, p->zName, strlen(p->zName)+1, p);
- assert( pOld==0 || pOld==p );
- }
- sqliteHashClear(&db->tblDrop);
- for(pElem=sqliteHashFirst(&db->idxHash); pElem; pElem=sqliteHashNext(pElem)){
- Index *pIndex = sqliteHashData(pElem);
- if( !pIndex->isCommit ){
- sqliteHashInsert(&toDelete, pIndex, 0, pIndex);
- }
- }
- for(pElem=sqliteHashFirst(&toDelete); pElem; pElem=sqliteHashNext(pElem)){
- Index *pIndex = sqliteHashData(pElem);
- sqliteUnlinkAndDeleteIndex(db, pIndex);
- }
- sqliteHashClear(&toDelete);
- for(pElem=sqliteHashFirst(&db->idxDrop); pElem; pElem=sqliteHashNext(pElem)){
- Index *pOld, *p = sqliteHashData(pElem);
- assert( p->isCommit );
- p->isDropped = 0;
- pOld = sqliteHashInsert(&db->idxHash, p->zName, strlen(p->zName)+1, p);
- assert( pOld==0 || pOld==p );
- }
- sqliteHashClear(&db->idxDrop);
-
- /* Remove any triggers that haven't been commited yet */
- for(pElem = sqliteHashFirst(&db->trigHash); pElem;
- pElem = (pElem?sqliteHashNext(pElem):0)){
- Trigger *pTrigger = sqliteHashData(pElem);
- if( !pTrigger->isCommit ){
- Table *pTbl = sqliteFindTable(db, pTrigger->table);
- if( pTbl ){
- if( pTbl->pTrigger == pTrigger ){
- pTbl->pTrigger = pTrigger->pNext;
- }else{
- Trigger *cc = pTbl->pTrigger;
- while( cc ){
- if( cc->pNext == pTrigger ){
- cc->pNext = cc->pNext->pNext;
- break;
- }
- cc = cc->pNext;
- }
- assert(cc);
- }
- }
- sqliteHashInsert(&db->trigHash, pTrigger->name,
- 1 + strlen(pTrigger->name), 0);
- sqliteDeleteTrigger(pTrigger);
- pElem = sqliteHashFirst(&db->trigHash);
- }
- }
-
- /* Any triggers that were dropped - put 'em back in place */
- for(pElem = sqliteHashFirst(&db->trigDrop); pElem;
- pElem = sqliteHashNext(pElem)){
- Trigger *pTrigger = sqliteHashData(pElem);
- Table *pTbl = sqliteFindTable(db, pTrigger->table);
- sqliteHashInsert(&db->trigHash, pTrigger->name,
- strlen(pTrigger->name) + 1, pTrigger);
- pTrigger->pNext = pTbl->pTrigger;
- pTbl->pTrigger = pTrigger;
- }
-
- sqliteHashClear(&db->trigDrop);
- db->flags &= ~SQLITE_InternChanges;
-}
-
/*
** Construct the name of a user table or index from a token.
**
@@ -376,13 +245,31 @@ char *sqliteTableNameFromToken(Token *pName){
return zName;
}
+/*
+** Generate code to open the appropriate master table. The table
+** opened will be SQLITE_MASTER for persistent tables and
+** SQLITE_TEMP_MASTER for temporary tables. The table is opened
+** on cursor 0.
+*/
+void sqliteOpenMasterTable(Vdbe *v, int isTemp){
+ if( isTemp ){
+ sqliteVdbeAddOp(v, OP_OpenWrAux, 0, 2);
+ sqliteVdbeChangeP3(v, -1, TEMP_MASTER_NAME, P3_STATIC);
+ }else{
+ sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2);
+ sqliteVdbeChangeP3(v, -1, MASTER_NAME, P3_STATIC);
+ }
+}
+
/*
** Begin constructing a new table representation in memory. This is
** the first of several action routines that get called in response
** to a CREATE TABLE statement. In particular, this routine is called
** after seeing tokens "CREATE" and "TABLE" and the table name. The
** pStart token is the CREATE and pName is the table name. The isTemp
-** flag is true if the "TEMP" or "TEMPORARY" keyword occurs in between
+** flag is true if the table should be stored in the auxiliary database
+** file instead of in the main database file. This is normally the case
+** when the "TEMP" or "TEMPORARY" keyword occurs in between
** CREATE and TABLE.
**
** The new table record is initialized and put in pParse->pNewTable.
@@ -408,7 +295,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName, int isTemp){
if( isTemp && db->pBeTemp==0 ){
int rc = sqliteBtreeOpen(0, 0, MAX_PAGES, &db->pBeTemp);
if( rc!=SQLITE_OK ){
- sqliteSetNString(&pParse->zErrMsg, "unable to open a temporary database "
+ sqliteSetString(&pParse->zErrMsg, "unable to open a temporary database "
"file for storing temporary tables", 0);
pParse->nErr++;
return;
@@ -482,13 +369,12 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName, int isTemp){
if( !isTemp ){
sqliteVdbeAddOp(v, OP_Integer, db->file_format, 0);
sqliteVdbeAddOp(v, OP_SetCookie, 0, 1);
- sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2);
- sqliteVdbeChangeP3(v, -1, MASTER_NAME, P3_STATIC);
- sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
- sqliteVdbeAddOp(v, OP_Dup, 0, 0);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
}
+ sqliteOpenMasterTable(v, isTemp);
+ sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
+ sqliteVdbeAddOp(v, OP_Dup, 0, 0);
+ sqliteVdbeAddOp(v, OP_String, 0, 0);
+ sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
}
}
@@ -725,10 +611,12 @@ void sqliteAddCollateType(Parse *pParse, int collType){
** and the probability of hitting the same cookie value is only
** 1 chance in 2^32. So we're safe enough.
*/
-void sqliteChangeCookie(sqlite *db){
+void sqliteChangeCookie(sqlite *db, Vdbe *v){
if( db->next_cookie==db->schema_cookie ){
db->next_cookie = db->schema_cookie + sqliteRandomByte() + 1;
db->flags |= SQLITE_InternChanges;
+ sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0);
+ sqliteVdbeAddOp(v, OP_SetCookie, 0, 0);
}
}
@@ -791,11 +679,10 @@ static char *createTableStmt(Table *p){
zSep2 = ",\n ";
zEnd = "\n)";
}
- n += 25 + 6*p->nCol;
+ n += 35 + 6*p->nCol;
zStmt = sqliteMalloc( n );
if( zStmt==0 ) return 0;
- assert( !p->isTemp );
- strcpy(zStmt, "CREATE TABLE ");
+ strcpy(zStmt, p->isTemp ? "CREATE TEMP TABLE " : "CREATE TABLE ");
k = strlen(zStmt);
identPut(zStmt, &k, p->zName);
zStmt[k++] = '(';
@@ -867,10 +754,10 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
}
/* If the initFlag is 1 it means we are reading the SQL off the
- ** "sqlite_master" table on the disk. So do not write to the disk
- ** again. Extract the root page number for the table from the
- ** pParse->newTnum field. (The page number should have been put
- ** there by the sqliteOpenCb routine.)
+ ** "sqlite_master" or "sqlite_temp_master" table on the disk.
+ ** So do not write to the disk again. Extract the root page number
+ ** for the table from the pParse->newTnum field. (The page number
+ ** should have been put there by the sqliteOpenCb routine.)
*/
if( pParse->initFlag ){
p->tnum = pParse->newTnum;
@@ -880,8 +767,8 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
** in the SQLITE_MASTER table of the database. The record number
** for the new table entry should already be on the stack.
**
- ** If this is a TEMPORARY table, then just create the table. Do not
- ** make an entry in SQLITE_MASTER.
+ ** If this is a TEMPORARY table, write the entry into the auxiliary
+ ** file instead of into the main database file.
*/
if( !pParse->initFlag ){
int n;
@@ -898,37 +785,35 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
}
p->tnum = 0;
- if( !p->isTemp ){
- sqliteVdbeAddOp(v, OP_Pull, 1, 0);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- if( p->pSelect==0 ){
- sqliteVdbeChangeP3(v, -1, "table", P3_STATIC);
- }else{
- sqliteVdbeChangeP3(v, -1, "view", P3_STATIC);
- }
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeChangeP3(v, -1, p->zName, P3_STATIC);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeChangeP3(v, -1, p->zName, P3_STATIC);
- sqliteVdbeAddOp(v, OP_Dup, 4, 0);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- if( pSelect ){
- char *z = createTableStmt(p);
- n = z ? strlen(z) : 0;
- sqliteVdbeChangeP3(v, -1, z, n);
- sqliteFree(z);
- }else{
- assert( pEnd!=0 );
- n = Addr(pEnd->z) - Addr(pParse->sFirstToken.z) + 1;
- sqliteVdbeChangeP3(v, -1, pParse->sFirstToken.z, n);
- }
- sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0);
- sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
- sqliteChangeCookie(db);
- sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0);
- sqliteVdbeAddOp(v, OP_SetCookie, 0, 0);
- sqliteVdbeAddOp(v, OP_Close, 0, 0);
+ sqliteVdbeAddOp(v, OP_Pull, 1, 0);
+ sqliteVdbeAddOp(v, OP_String, 0, 0);
+ if( p->pSelect==0 ){
+ sqliteVdbeChangeP3(v, -1, "table", P3_STATIC);
+ }else{
+ sqliteVdbeChangeP3(v, -1, "view", P3_STATIC);
}
+ sqliteVdbeAddOp(v, OP_String, 0, 0);
+ sqliteVdbeChangeP3(v, -1, p->zName, P3_STATIC);
+ sqliteVdbeAddOp(v, OP_String, 0, 0);
+ sqliteVdbeChangeP3(v, -1, p->zName, P3_STATIC);
+ sqliteVdbeAddOp(v, OP_Dup, 4, 0);
+ sqliteVdbeAddOp(v, OP_String, 0, 0);
+ if( pSelect ){
+ char *z = createTableStmt(p);
+ n = z ? strlen(z) : 0;
+ sqliteVdbeChangeP3(v, -1, z, n);
+ sqliteFree(z);
+ }else{
+ assert( pEnd!=0 );
+ n = Addr(pEnd->z) - Addr(pParse->sFirstToken.z) + 1;
+ sqliteVdbeChangeP3(v, -1, pParse->sFirstToken.z, n);
+ }
+ sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0);
+ sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
+ if( !p->isTemp ){
+ sqliteChangeCookie(db, v);
+ }
+ sqliteVdbeAddOp(v, OP_Close, 0, 0);
if( pSelect ){
int op = p->isTemp ? OP_OpenWrAux : OP_OpenWrite;
sqliteVdbeAddOp(v, op, 1, 0);
@@ -1151,34 +1036,38 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){
v = sqliteGetVdbe(pParse);
if( v ){
static VdbeOp dropTable[] = {
- { OP_OpenWrite, 0, 2, MASTER_NAME},
- { OP_Rewind, 0, ADDR(9), 0},
- { OP_String, 0, 0, 0}, /* 2 */
+ { OP_Rewind, 0, ADDR(8), 0},
+ { OP_String, 0, 0, 0}, /* 1 */
{ OP_MemStore, 1, 1, 0},
- { OP_MemLoad, 1, 0, 0}, /* 4 */
+ { OP_MemLoad, 1, 0, 0}, /* 3 */
{ OP_Column, 0, 2, 0},
- { OP_Ne, 0, ADDR(8), 0},
+ { OP_Ne, 0, ADDR(7), 0},
{ OP_Delete, 0, 0, 0},
- { OP_Next, 0, ADDR(4), 0}, /* 8 */
- { OP_Integer, 0, 0, 0}, /* 9 */
- { OP_SetCookie, 0, 0, 0},
- { OP_Close, 0, 0, 0},
+ { OP_Next, 0, ADDR(3), 0}, /* 7 */
};
Index *pIdx;
+ Trigger *pTrigger;
sqliteBeginWriteOperation(pParse, 0);
+ sqliteOpenMasterTable(v, pTable->isTemp);
/* Drop all triggers associated with the table being dropped */
- while( pTable->pTrigger ){
+ pTrigger = pTable->pTrigger;
+ while( pTrigger ){
Token tt;
tt.z = pTable->pTrigger->name;
tt.n = strlen(pTable->pTrigger->name);
sqliteDropTrigger(pParse, &tt, 1);
+ if( pParse->explain ){
+ pTrigger = pTrigger->pNext;
+ }else{
+ pTrigger = pTable->pTrigger;
+ }
}
+ base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
+ sqliteVdbeChangeP3(v, base+1, pTable->zName, 0);
if( !pTable->isTemp ){
- base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
- sqliteVdbeChangeP3(v, base+2, pTable->zName, 0);
- sqliteChangeCookie(db);
- sqliteVdbeChangeP1(v, base+9, db->next_cookie);
+ sqliteChangeCookie(db, v);
}
+ sqliteVdbeAddOp(v, OP_Close, 0, 0);
if( !isView ){
sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->isTemp);
for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){
@@ -1188,16 +1077,13 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){
sqliteEndWriteOperation(pParse);
}
- /* Move the table (and all its indices) to the pending DROP queue.
- ** Or, if the table was never committed, just delete it. If the table
- ** has been committed and is placed on the pending DROP queue, then the
- ** delete will occur when sqliteCommitInternalChanges() executes.
+ /* Delete the in-memory description of the table.
**
** Exception: if the SQL statement began with the EXPLAIN keyword,
** then no changes should be made.
*/
if( !pParse->explain ){
- sqlitePendingDropTable(db, pTable);
+ sqliteUnlinkAndDeleteTable(db, pTable);
db->flags |= SQLITE_InternChanges;
}
sqliteViewResetAll(db);
@@ -1264,7 +1150,7 @@ void sqliteCreateIndex(
** Since its table has been suppressed, we need to also suppress the
** index.
*/
- if( pParse->initFlag && pTab->isTemp ){
+ if( pParse->initFlag && !pParse->isTemp && pTab->isTemp ){
goto exit_create_index;
}
@@ -1429,40 +1315,33 @@ void sqliteCreateIndex(
if( v==0 ) goto exit_create_index;
if( pTable!=0 ){
sqliteBeginWriteOperation(pParse, 0);
- if( !isTemp ){
- sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2);
- sqliteVdbeChangeP3(v, -1, MASTER_NAME, P3_STATIC);
- }
- }
- if( !isTemp ){
- sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeChangeP3(v, -1, "index", P3_STATIC);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeChangeP3(v, -1, pIndex->zName, P3_STATIC);
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
+ sqliteOpenMasterTable(v, isTemp);
}
+ sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
+ sqliteVdbeAddOp(v, OP_String, 0, 0);
+ sqliteVdbeChangeP3(v, -1, "index", P3_STATIC);
+ sqliteVdbeAddOp(v, OP_String, 0, 0);
+ sqliteVdbeChangeP3(v, -1, pIndex->zName, P3_STATIC);
+ sqliteVdbeAddOp(v, OP_String, 0, 0);
+ sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
addr = sqliteVdbeAddOp(v, OP_CreateIndex, 0, isTemp);
sqliteVdbeChangeP3(v, addr, (char*)&pIndex->tnum, P3_POINTER);
pIndex->tnum = 0;
if( pTable ){
+ sqliteVdbeAddOp(v, OP_Dup, 0, 0);
if( isTemp ){
sqliteVdbeAddOp(v, OP_OpenWrAux, 1, 0);
}else{
- sqliteVdbeAddOp(v, OP_Dup, 0, 0);
sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0);
}
}
- if( !isTemp ){
- addr = sqliteVdbeAddOp(v, OP_String, 0, 0);
- if( pStart && pEnd ){
- n = Addr(pEnd->z) - Addr(pStart->z) + 1;
- sqliteVdbeChangeP3(v, addr, pStart->z, n);
- }
- sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0);
- sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
+ addr = sqliteVdbeAddOp(v, OP_String, 0, 0);
+ if( pStart && pEnd ){
+ n = Addr(pEnd->z) - Addr(pStart->z) + 1;
+ sqliteVdbeChangeP3(v, addr, pStart->z, n);
}
+ sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0);
+ sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
if( pTable ){
sqliteVdbeAddOp(v, isTemp ? OP_OpenAux : OP_Open, 2, pTab->tnum);
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
@@ -1481,11 +1360,9 @@ void sqliteCreateIndex(
}
if( pTable!=0 ){
if( !isTemp ){
- sqliteChangeCookie(db);
- sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0);
- sqliteVdbeAddOp(v, OP_SetCookie, 0, 0);
- sqliteVdbeAddOp(v, OP_Close, 0, 0);
+ sqliteChangeCookie(db, v);
}
+ sqliteVdbeAddOp(v, OP_Close, 0, 0);
sqliteEndWriteOperation(pParse);
}
}
@@ -1523,42 +1400,35 @@ void sqliteDropIndex(Parse *pParse, Token *pName){
v = sqliteGetVdbe(pParse);
if( v ){
static VdbeOp dropIndex[] = {
- { OP_OpenWrite, 0, 2, MASTER_NAME},
- { OP_Rewind, 0, ADDR(10),0},
- { OP_String, 0, 0, 0}, /* 2 */
+ { OP_Rewind, 0, ADDR(9), 0},
+ { OP_String, 0, 0, 0}, /* 1 */
{ OP_MemStore, 1, 1, 0},
- { OP_MemLoad, 1, 0, 0}, /* 4 */
+ { OP_MemLoad, 1, 0, 0}, /* 3 */
{ OP_Column, 0, 1, 0},
- { OP_Eq, 0, ADDR(9), 0},
- { OP_Next, 0, ADDR(4), 0},
- { OP_Goto, 0, ADDR(10),0},
- { OP_Delete, 0, 0, 0}, /* 9 */
- { OP_Integer, 0, 0, 0}, /* 10 */
- { OP_SetCookie, 0, 0, 0},
- { OP_Close, 0, 0, 0},
+ { OP_Eq, 0, ADDR(8), 0},
+ { OP_Next, 0, ADDR(3), 0},
+ { OP_Goto, 0, ADDR(9), 0},
+ { OP_Delete, 0, 0, 0}, /* 8 */
};
int base;
Table *pTab = pIndex->pTable;
sqliteBeginWriteOperation(pParse, 0);
+ sqliteOpenMasterTable(v, pTab->isTemp);
+ base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
+ sqliteVdbeChangeP3(v, base+1, pIndex->zName, 0);
if( !pTab->isTemp ){
- base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
- sqliteVdbeChangeP3(v, base+2, pIndex->zName, P3_STATIC);
- sqliteChangeCookie(db);
- sqliteVdbeChangeP1(v, base+10, db->next_cookie);
+ sqliteChangeCookie(db, v);
}
+ sqliteVdbeAddOp(v, OP_Close, 0, 0);
sqliteVdbeAddOp(v, OP_Destroy, pIndex->tnum, pTab->isTemp);
sqliteEndWriteOperation(pParse);
}
- /* Move the index onto the pending DROP queue. Or, if the index was
- ** never committed, just delete it. Indices on the pending DROP queue
- ** get deleted by sqliteCommitInternalChanges() when the user executes
- ** a COMMIT. Or if a rollback occurs, the elements of the DROP queue
- ** are moved back into the main hash table.
+ /* Delete the in-memory description of this index.
*/
if( !pParse->explain ){
- sqlitePendingDropIndex(db, pIndex);
+ sqliteUnlinkAndDeleteIndex(db, pIndex);
db->flags |= SQLITE_InternChanges;
}
}
diff --git a/src/main.c b/src/main.c
index 436afc848e..e6877b38fa 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.83 2002/06/22 02:33:38 drh Exp $
+** $Id: main.c,v 1.84 2002/06/25 01:09:11 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -30,6 +30,7 @@
** argv[1] = table or index name or meta statement type.
** argv[2] = root page number for table or index. NULL for meta.
** argv[3] = SQL create statement for the table or index
+** argv[4] = "1" for temporary files, "0" for main database
**
*/
int sqliteInitCallback(void *pDb, int argc, char **argv, char **azColName){
@@ -41,29 +42,8 @@ int sqliteInitCallback(void *pDb, int argc, char **argv, char **azColName){
** make sure fields do not contain NULLs. Otherwise we might core
** when attempting to initialize from a corrupt database file. */
- assert( argc==4 );
+ assert( argc==5 );
switch( argv[0][0] ){
- case 'c': { /* Recommended pager cache size */
- int size = atoi(argv[3]);
- if( size==0 ){ size = MAX_PAGES; }
- db->cache_size = size;
- sqliteBtreeSetCacheSize(db->pBe, size);
- break;
- }
- case 'f': { /* File format */
- /*
- ** file_format==1 Version 2.1.0.
- ** file_format==2 Version 2.2.0. Integer primary key.
- ** file_format==3 Version 2.6.0. Separate text and numeric datatypes.
- */
- db->file_format = atoi(argv[3]);
- break;
- }
- case 's': { /* Schema cookie */
- db->schema_cookie = atoi(argv[3]);
- db->next_cookie = db->schema_cookie;
- break;
- }
case 'v':
case 'i':
case 't': { /* CREATE TABLE, CREATE INDEX, or CREATE VIEW statements */
@@ -76,6 +56,7 @@ int sqliteInitCallback(void *pDb, int argc, char **argv, char **azColName){
memset(&sParse, 0, sizeof(sParse));
sParse.db = db;
sParse.initFlag = 1;
+ sParse.isTemp = argv[4][0] - '0';
sParse.newTnum = atoi(argv[2]);
sqliteRunParser(&sParse, argv[3], 0);
}else{
@@ -120,15 +101,29 @@ int sqliteInitCallback(void *pDb, int argc, char **argv, char **azColName){
** has the sqlite_master table locked) than another attempt
** is made the first time the database is accessed.
*/
-static int sqliteInit(sqlite *db, char **pzErrMsg){
- Vdbe *vdbe;
+int sqliteInit(sqlite *db, char **pzErrMsg){
int rc;
+ BtCursor *curMain;
+ int size;
+ Table *pTab;
+ char *azArg[6];
+ int meta[SQLITE_N_BTREE_META];
+ Parse sParse;
/*
** The master database table has a structure like this
*/
static char master_schema[] =
- "CREATE TABLE " MASTER_NAME " (\n"
+ "CREATE TABLE sqlite_master(\n"
+ " type text,\n"
+ " name text,\n"
+ " tbl_name text,\n"
+ " rootpage integer,\n"
+ " sql text\n"
+ ")"
+ ;
+ static char temp_master_schema[] =
+ "CREATE TEMP TABLE sqlite_temp_master(\n"
" type text,\n"
" name text,\n"
" tbl_name text,\n"
@@ -137,165 +132,117 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
")"
;
- /* The following VDBE program is used to initialize the internal
- ** structure holding the tables and indexes of the database.
- ** The database contains a special table named "sqlite_master"
- ** defined as follows:
+ /* The following SQL will read the schema from the master tables.
+ ** The first version works with SQLite file formats 2 or greater.
+ ** The second version is for format 1 files.
**
- ** CREATE TABLE sqlite_master (
- ** type text, -- Either "table" or "index" or "meta"
- ** name text, -- Name of table or index
- ** tbl_name text, -- Associated table
- ** rootpage integer, -- The integer page number of root page
- ** sql text -- The CREATE statement for this object
- ** );
- **
- ** The sqlite_master table contains a single entry for each table
- ** and each index. The "type" column tells whether the entry is
- ** a table or index. The "name" column is the name of the object.
- ** The "tbl_name" is the name of the associated table. For tables,
- ** the tbl_name column is always the same as name. For indices, the
- ** tbl_name column contains the name of the table that the index
- ** indexes. The "rootpage" column holds the number of the root page
- ** for the b-tree for the table or index. Finally, the "sql" column
- ** contains the complete text of the CREATE TABLE or CREATE INDEX
- ** statement that originally created the table or index. If an index
- ** was created to fulfill a PRIMARY KEY or UNIQUE constraint on a table,
- ** then the "sql" column is NULL.
- **
- ** In format 1, entries in the sqlite_master table are in a random
- ** order. Two passes must be made through the table to initialize
- ** internal data structures. The first pass reads table definitions
- ** and the second pass read index definitions. Having two passes
- ** insures that indices appear after their tables.
- **
- ** In format 2, entries appear in chronological order. Only a single
- ** pass needs to be made through the table since everything will be
- ** in the write order. VIEWs may only occur in format 2.
- **
- ** The following program invokes its callback on the SQL for each
- ** table then goes back and invokes the callback on the
- ** SQL for each index. The callback will invoke the
- ** parser to build the internal representation of the
- ** database scheme.
+ ** Beginning with file format 2, the rowid for new table entries
+ ** (including entries in sqlite_master) is an increasing integer.
+ ** So for file format 2 and later, we can play back sqlite_master
+ ** and all the CREATE statements will appear in the right order.
+ ** But with file format 1, table entries were random and so we
+ ** have to make sure the CREATE TABLEs occur before their corresponding
+ ** CREATE INDEXs. (We don't have to deal with CREATE VIEW or
+ ** CREATE TRIGGER in file format 1 because those constructs did
+ ** not exist then.)
*/
- static VdbeOp initProg[] = {
- /* Send the file format to the callback routine
- */
- { OP_Open, 0, 2, 0},
- { OP_String, 0, 0, "file-format"},
- { OP_String, 0, 0, 0},
- { OP_String, 0, 0, 0},
- { OP_ReadCookie, 0, 1, 0},
- { OP_Callback, 4, 0, 0},
+ static char init_script[] =
+ "SELECT type, name, rootpage, sql, 1 FROM sqlite_temp_master "
+ "UNION ALL "
+ "SELECT type, name, rootpage, sql, 0 FROM sqlite_master";
+ static char older_init_script[] =
+ "SELECT type, name, rootpage, sql, 1 FROM sqlite_temp_master "
+ "UNION ALL "
+ "SELECT type, name, rootpage, sql, 0 FROM sqlite_master "
+ "WHERE type='table' "
+ "UNION ALL "
+ "SELECT type, name, rootpage, sql, 0 FROM sqlite_master "
+ "WHERE type='index'";
- /* Send the recommended pager cache size to the callback routine
- */
- { OP_String, 0, 0, "cache-size"},
- { OP_String, 0, 0, 0},
- { OP_String, 0, 0, 0},
- { OP_ReadCookie, 0, 2, 0},
- { OP_Callback, 4, 0, 0},
- /* Send the initial schema cookie to the callback
- */
- { OP_String, 0, 0, "schema_cookie"},
- { OP_String, 0, 0, 0},
- { OP_String, 0, 0, 0},
- { OP_ReadCookie, 0, 0, 0},
- { OP_Callback, 4, 0, 0},
-
- /* Check the file format. If the format number is 2 or more,
- ** then do a single pass through the SQLITE_MASTER table. For
- ** a format number of less than 2, jump forward to a different
- ** algorithm that makes two passes through the SQLITE_MASTER table,
- ** once for tables and a second time for indices.
- */
- { OP_ReadCookie, 0, 1, 0},
- { OP_Integer, 2, 0, 0},
- { OP_Lt, 0, 28, 0},
-
- /* This is the code for doing a single scan through the SQLITE_MASTER
- ** table. This code runs for format 2 and greater.
- */
- { OP_Rewind, 0, 26, 0},
- { OP_Column, 0, 0, 0}, /* 20 */
- { OP_Column, 0, 1, 0},
- { OP_Column, 0, 3, 0},
- { OP_Column, 0, 4, 0},
- { OP_Callback, 4, 0, 0},
- { OP_Next, 0, 20, 0},
- { OP_Close, 0, 0, 0}, /* 26 */
- { OP_Halt, 0, 0, 0},
-
- /* This is the code for doing two passes through SQLITE_MASTER. This
- ** code runs for file format 1.
- */
- { OP_Rewind, 0, 48, 0}, /* 28 */
- { OP_Column, 0, 0, 0}, /* 29 */
- { OP_String, 0, 0, "table"},
- { OP_Ne, 0, 37, 0},
- { OP_Column, 0, 0, 0},
- { OP_Column, 0, 1, 0},
- { OP_Column, 0, 3, 0},
- { OP_Column, 0, 4, 0},
- { OP_Callback, 4, 0, 0},
- { OP_Next, 0, 29, 0}, /* 37 */
- { OP_Rewind, 0, 48, 0}, /* 38 */
- { OP_Column, 0, 0, 0}, /* 39 */
- { OP_String, 0, 0, "index"},
- { OP_Ne, 0, 47, 0},
- { OP_Column, 0, 0, 0},
- { OP_Column, 0, 1, 0},
- { OP_Column, 0, 3, 0},
- { OP_Column, 0, 4, 0},
- { OP_Callback, 4, 0, 0},
- { OP_Next, 0, 39, 0}, /* 47 */
- { OP_Close, 0, 0, 0}, /* 48 */
- { OP_Halt, 0, 0, 0},
- };
-
- /* Create a virtual machine to run the initialization program. Run
- ** the program. Then delete the virtual machine.
+ /* Construct the schema tables: sqlite_master and sqlite_temp_master
*/
- vdbe = sqliteVdbeCreate(db);
- if( vdbe==0 ){
- sqliteSetString(pzErrMsg, "out of memory", 0);
- return SQLITE_NOMEM;
+ azArg[0] = "table";
+ azArg[1] = MASTER_NAME;
+ azArg[2] = "2";
+ azArg[3] = master_schema;
+ azArg[4] = "0";
+ azArg[5] = 0;
+ sqliteInitCallback(db, 5, azArg, 0);
+ pTab = sqliteFindTable(db, MASTER_NAME);
+ if( pTab ){
+ pTab->readOnly = 1;
}
- sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg);
- rc = sqliteVdbeExec(vdbe, sqliteInitCallback, db, pzErrMsg,
- db->pBusyArg, db->xBusyCallback);
- sqliteVdbeDelete(vdbe);
- if( rc==SQLITE_OK && db->nTable==0 ){
+ azArg[1] = TEMP_MASTER_NAME;
+ azArg[3] = temp_master_schema;
+ azArg[4] = "1";
+ sqliteInitCallback(db, 5, azArg, 0);
+ pTab = sqliteFindTable(db, TEMP_MASTER_NAME);
+ if( pTab ){
+ pTab->readOnly = 1;
+ }
+
+ /* Create a cursor to hold the database open
+ */
+ if( db->pBe==0 ) return SQLITE_OK;
+ rc = sqliteBtreeCursor(db->pBe, 2, 0, &curMain);
+ if( rc ) return rc;
+
+ /* Get the database meta information
+ */
+ rc = sqliteBtreeGetMeta(db->pBe, meta);
+ if( rc ){
+ sqliteBtreeCloseCursor(curMain);
+ return rc;
+ }
+ db->schema_cookie = meta[1];
+ db->next_cookie = db->schema_cookie;
+ db->file_format = meta[2];
+ size = meta[3];
+ if( size==0 ){ size = MAX_PAGES; }
+ db->cache_size = size;
+ sqliteBtreeSetCacheSize(db->pBe, size);
+
+ /*
+ ** file_format==1 Version 2.1.0.
+ ** file_format==2 Version 2.2.0. Add support for INTEGER PRIMARY KEY.
+ ** file_format==3 Version 2.6.0. Add support for separate numeric and
+ ** text datatypes.
+ */
+ if( db->file_format==0 ){
db->file_format = 2;
- }
- if( rc==SQLITE_OK && db->file_format>2 ){
+ }else if( db->file_format>2 ){
+ sqliteBtreeCloseCursor(curMain);
sqliteSetString(pzErrMsg, "unsupported file format", 0);
rc = SQLITE_ERROR;
}
- /* The schema for the SQLITE_MASTER table is not stored in the
- ** database itself. We have to invoke the callback one extra
- ** time to get it to process the SQLITE_MASTER table defintion.
+ /* Read the schema information out of the schema tables
*/
- if( rc==SQLITE_OK ){
- Table *pTab;
- char *azArg[6];
- azArg[0] = "table";
- azArg[1] = MASTER_NAME;
- azArg[2] = "2";
- azArg[3] = master_schema;
- azArg[4] = 0;
- sqliteInitCallback(db, 4, azArg, 0);
- pTab = sqliteFindTable(db, MASTER_NAME);
- if( pTab ){
- pTab->readOnly = 1;
- }
+ memset(&sParse, 0, sizeof(sParse));
+ sParse.db = db;
+ sParse.pBe = db->pBe;
+ sParse.xCallback = sqliteInitCallback;
+ sParse.pArg = (void*)db;
+ sParse.initFlag = 1;
+ sqliteRunParser(&sParse,
+ db->file_format>=2 ? init_script : older_init_script,
+ pzErrMsg);
+ if( sqlite_malloc_failed ){
+ sqliteSetString(pzErrMsg, "out of memory", 0);
+ sParse.rc = SQLITE_NOMEM;
+ sqliteBtreeRollback(db->pBe);
+ sqliteResetInternalSchema(db);
+ }
+ if( sParse.rc==SQLITE_OK ){
db->flags |= SQLITE_Initialized;
sqliteCommitInternalChanges(db);
+ }else{
+ db->flags &= ~SQLITE_Initialized;
+ sqliteResetInternalSchema(db);
}
- return rc;
+ sqliteBtreeCloseCursor(curMain);
+ return sParse.rc;
}
/*
@@ -333,9 +280,6 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
sqliteHashInit(&db->tblHash, SQLITE_HASH_STRING, 0);
sqliteHashInit(&db->idxHash, SQLITE_HASH_STRING, 0);
sqliteHashInit(&db->trigHash, SQLITE_HASH_STRING, 0);
- sqliteHashInit(&db->trigDrop, SQLITE_HASH_STRING, 0);
- sqliteHashInit(&db->tblDrop, SQLITE_HASH_POINTER, 0);
- sqliteHashInit(&db->idxDrop, SQLITE_HASH_POINTER, 0);
sqliteHashInit(&db->aFunc, SQLITE_HASH_STRING, 1);
sqliteRegisterBuiltinFunctions(db);
db->onError = OE_Default;
@@ -377,74 +321,6 @@ no_mem_on_open:
return 0;
}
-/*
-** Erase all schema information from the schema hash table. Except
-** tables that are created using CREATE TEMPORARY TABLE are preserved
-** if the preserveTemps flag is true.
-**
-** The database schema is normally read in once when the database
-** is first opened and stored in a hash table in the sqlite structure.
-** This routine erases the stored schema. This erasure occurs because
-** either the database is being closed or because some other process
-** changed the schema and this process needs to reread it.
-*/
-static void clearHashTable(sqlite *db, int preserveTemps){
- HashElem *pElem;
- Hash temp1;
- Hash temp2;
-
- /* Make sure there are no uncommited DROPs */
- assert( sqliteHashFirst(&db->tblDrop)==0 || sqlite_malloc_failed );
- assert( sqliteHashFirst(&db->idxDrop)==0 || sqlite_malloc_failed );
- assert( sqliteHashFirst(&db->trigDrop)==0 || sqlite_malloc_failed );
- temp1 = db->tblHash;
- temp2 = db->trigHash;
- sqliteHashInit(&db->trigHash, SQLITE_HASH_STRING, 0);
- sqliteHashClear(&db->idxHash);
-
- for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
- Trigger * pTrigger = sqliteHashData(pElem);
- Table *pTab = sqliteFindTable(db, pTrigger->table);
- assert(pTab);
- if( pTab->isTemp && preserveTemps ){
- sqliteHashInsert(&db->trigHash, pTrigger->name, strlen(pTrigger->name),
- pTrigger);
- }else{
- sqliteDeleteTrigger(pTrigger);
- }
- }
- sqliteHashClear(&temp2);
-
- sqliteHashInit(&db->tblHash, SQLITE_HASH_STRING, 0);
-
- for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
- Table *pTab = sqliteHashData(pElem);
- if( preserveTemps && pTab->isTemp ){
- Index *pIdx;
- int nName = strlen(pTab->zName);
- Table *pOld = sqliteHashInsert(&db->tblHash, pTab->zName, nName+1, pTab);
- if( pOld!=0 ){
- assert( pOld==pTab ); /* Malloc failed on the HashInsert */
- sqliteDeleteTable(db, pOld);
- continue;
- }
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- int n = strlen(pIdx->zName)+1;
- Index *pOldIdx;
- pOldIdx = sqliteHashInsert(&db->idxHash, pIdx->zName, n, pIdx);
- if( pOld ){
- assert( pOldIdx==pIdx );
- sqliteUnlinkAndDeleteIndex(db, pOldIdx);
- }
- }
- }else{
- sqliteDeleteTable(db, pTab);
- }
- }
- sqliteHashClear(&temp1);
- db->flags &= ~SQLITE_Initialized;
-}
-
/*
** Return the ROWID of the most recent insert
*/
@@ -467,8 +343,7 @@ void sqlite_close(sqlite *db){
if( sqliteSafetyCheck(db) || sqliteSafetyOn(db) ){ return; }
db->magic = SQLITE_MAGIC_CLOSED;
sqliteBtreeClose(db->pBe);
- sqliteRollbackInternalChanges(db);
- clearHashTable(db, 0);
+ sqliteResetInternalSchema(db);
if( db->pBeTemp ){
sqliteBtreeClose(db->pBeTemp);
}
@@ -638,11 +513,11 @@ int sqlite_exec(
sqliteBtreeRollback(db->pBe);
if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp);
db->flags &= ~SQLITE_InTrans;
- clearHashTable(db, 0);
+ sqliteResetInternalSchema(db);
}
sqliteStrRealloc(pzErrMsg);
if( sParse.rc==SQLITE_SCHEMA ){
- clearHashTable(db, 1);
+ sqliteResetInternalSchema(db);
}
db->recursionDepth--;
if( sqliteSafetyOff(db) ) goto exec_misuse;
diff --git a/src/parse.y b/src/parse.y
index 725ea06a29..5ea48dfe3a 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
-** @(#) $Id: parse.y,v 1.74 2002/06/17 17:07:20 drh Exp $
+** @(#) $Id: parse.y,v 1.75 2002/06/25 01:09:12 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
@@ -65,9 +65,9 @@ input ::= cmdlist.
cmdlist ::= ecmd.
cmdlist ::= cmdlist ecmd.
ecmd ::= explain cmd SEMI. {sqliteExec(pParse);}
-ecmd ::= cmd SEMI. {sqliteExec(pParse);}
ecmd ::= SEMI.
-explain ::= EXPLAIN. {pParse->explain = 1;}
+explain ::= EXPLAIN. { sqliteBeginParse(pParse, 1); }
+explain ::= . { sqliteBeginParse(pParse, 0); }
///////////////////// Begin and end transactions. ////////////////////////////
//
@@ -87,8 +87,8 @@ create_table ::= CREATE(X) temp(T) TABLE ids(Y). {
sqliteStartTable(pParse,&X,&Y,T);
}
%type temp {int}
-temp(A) ::= TEMP. {A = 1;}
-temp(A) ::= . {A = 0;}
+temp(A) ::= TEMP. {A = pParse->isTemp || !pParse->initFlag;}
+temp(A) ::= . {A = pParse->isTemp;}
create_table_args ::= LP columnlist conslist_opt RP(X). {
sqliteEndTable(pParse,&X,0);
}
diff --git a/src/shell.c b/src/shell.c
index 63567c4f80..d8ee2108e0 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -12,7 +12,7 @@
** This file contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.
**
-** $Id: shell.c,v 1.57 2002/05/21 13:02:24 drh Exp $
+** $Id: shell.c,v 1.58 2002/06/25 01:09:12 drh Exp $
*/
#include
#include
@@ -660,8 +660,11 @@ static int do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
sqlite_exec_printf(db,
"SELECT name FROM sqlite_master "
"WHERE type='index' AND tbl_name LIKE '%q' "
- "ORDER BY name",
- callback, &data, &zErrMsg, azArg[1]
+ "UNION ALL "
+ "SELECT name FROM sqlite_temp_master "
+ "WHERE type='index' AND tbl_name LIKE '%q' "
+ "ORDER BY 1",
+ callback, &data, &zErrMsg, azArg[1], azArg[1]
);
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
@@ -796,18 +799,35 @@ static int do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
new_colv[0] = "sql";
new_colv[1] = 0;
callback(&data, 1, new_argv, new_colv);
+ }else if( sqliteStrICmp(azArg[1],"sqlite_temp_master")==0 ){
+ char *new_argv[2], *new_colv[2];
+ new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n"
+ " type text,\n"
+ " name text,\n"
+ " tbl_name text,\n"
+ " rootpage integer,\n"
+ " sql text\n"
+ ")";
+ new_argv[1] = 0;
+ new_colv[0] = "sql";
+ new_colv[1] = 0;
+ callback(&data, 1, new_argv, new_colv);
}else{
sqlite_exec_printf(db,
- "SELECT sql FROM sqlite_master "
+ "SELECT sql FROM "
+ " (SELECT * FROM sqlite_master UNION ALL"
+ " SELECT * FROM sqlite_temp_master) "
"WHERE tbl_name LIKE '%q' AND type!='meta' AND sql NOTNULL "
- "ORDER BY type DESC, name",
+ "ORDER BY substr(type,2,1), name",
callback, &data, &zErrMsg, azArg[1]);
}
}else{
sqlite_exec(db,
- "SELECT sql FROM sqlite_master "
+ "SELECT sql FROM "
+ " (SELECT * FROM sqlite_master UNION ALL"
+ " SELECT * FROM sqlite_temp_master) "
"WHERE type!='meta' AND sql NOTNULL "
- "ORDER BY tbl_name, type DESC, name",
+ "ORDER BY substr(type,2,1), name",
callback, &data, &zErrMsg
);
}
@@ -846,15 +866,21 @@ static int do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
rc = sqlite_get_table(db,
"SELECT name FROM sqlite_master "
"WHERE type IN ('table','view') "
- "ORDER BY name",
+ "UNION ALL "
+ "SELECT name FROM sqlite_temp_master "
+ "WHERE type IN ('table','view') "
+ "ORDER BY 1",
&azResult, &nRow, 0, &zErrMsg
);
}else{
rc = sqlite_get_table_printf(db,
"SELECT name FROM sqlite_master "
"WHERE type IN ('table','view') AND name LIKE '%%%q%%' "
- "ORDER BY name",
- &azResult, &nRow, 0, &zErrMsg, azArg[1]
+ "UNION ALL "
+ "SELECT name FROM sqlite_temp_master "
+ "WHERE type IN ('table','view') AND name LIKE '%%%q%%' "
+ "ORDER BY 1",
+ &azResult, &nRow, 0, &zErrMsg, azArg[1], azArg[1]
);
}
if( zErrMsg ){
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 67e7ad6cf9..3f1d801177 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.130 2002/06/24 22:01:58 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.131 2002/06/25 01:09:12 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
@@ -145,7 +145,8 @@ extern int sqlite_iMallocFail; /* Fail sqliteMalloc() after this many calls */
** is a special table that holds the names and attributes of all
** user tables and indices.
*/
-#define MASTER_NAME "sqlite_master"
+#define MASTER_NAME "sqlite_master"
+#define TEMP_MASTER_NAME "sqlite_temp_master"
/*
** A convenience macro that returns the number of elements in
@@ -202,8 +203,7 @@ struct sqlite {
int (*xBusyCallback)(void *,const char*,int); /* The busy callback */
Hash tblHash; /* All tables indexed by name */
Hash idxHash; /* All (named) indices indexed by name */
- Hash tblDrop; /* Uncommitted DROP TABLEs */
- Hash idxDrop; /* Uncommitted DROP INDEXs */
+ Hash trigHash; /* All triggers indexed by name */
Hash aFunc; /* All functions that can be in SQL exprs */
int lastRowid; /* ROWID of most recent insert */
int priorNewRowid; /* Last randomly generated ROWID */
@@ -211,9 +211,6 @@ struct sqlite {
int magic; /* Magic number for detect library misuse */
int nChange; /* Number of rows changed */
int recursionDepth; /* Number of nested calls to sqlite_exec() */
-
- Hash trigHash; /* All triggers indexed by name */
- Hash trigDrop; /* Uncommited dropped triggers */
};
/*
@@ -325,12 +322,10 @@ struct Table {
int tnum; /* Root BTree node for this table (see note above) */
Select *pSelect; /* NULL for tables. Points to definition if a view. */
u8 readOnly; /* True if this table should not be written by the user */
- u8 isCommit; /* True if creation of this table has been committed */
u8 isTemp; /* True if stored in db->pBeTemp instead of db->pBe */
u8 isTransient; /* True if automatically deleted when VDBE finishes */
u8 hasPrimKey; /* True if there exists a primary key */
u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
-
Trigger *pTrigger; /* List of SQL triggers on this table */
};
@@ -386,8 +381,6 @@ struct Index {
Table *pTable; /* The SQL table being indexed */
int tnum; /* Page containing root of this index in database file */
u8 isUnique; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
- u8 isCommit; /* True if creation of this index has been committed */
- u8 isDropped; /* True if a DROP INDEX has executed on this index */
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
Index *pNext; /* The next index associated with the same table */
};
@@ -648,10 +641,15 @@ struct Parse {
Token sLastToken; /* The last token parsed */
Table *pNewTable; /* A table being constructed by CREATE TABLE */
Vdbe *pVdbe; /* An engine for executing database bytecode */
- int colNamesSet; /* TRUE after OP_ColumnCount has been issued to pVdbe */
- int explain; /* True if the EXPLAIN flag is found on the query */
- int initFlag; /* True if reparsing CREATE TABLEs */
- int nameClash; /* A permanent table name clashes with temp table name */
+ u8 colNamesSet; /* TRUE after OP_ColumnCount has been issued to pVdbe */
+ u8 explain; /* True if the EXPLAIN flag is found on the query */
+ u8 initFlag; /* True if reparsing CREATE TABLEs */
+ u8 nameClash; /* A permanent table name clashes with temp table name */
+ u8 useAgg; /* If true, extract field values from the aggregator
+ ** while generating expressions. Normally false */
+ u8 schemaVerified; /* True if an OP_VerifySchema has been coded someplace
+ ** other than after an OP_Transaction */
+ u8 isTemp; /* True if parsing temporary tables */
int newTnum; /* Table number to use when reparsing CREATE TABLEs */
int nErr; /* Number of errors seen */
int nTab; /* Number of previously allocated VDBE cursors */
@@ -659,10 +657,6 @@ struct Parse {
int nSet; /* Number of sets used so far */
int nAgg; /* Number of aggregate expressions */
AggExpr *aAgg; /* An array of aggregate expressions */
- int useAgg; /* If true, extract field values from the aggregator
- ** while generating expressions. Normally false */
- int schemaVerified; /* True if an OP_VerifySchema has been coded someplace
- ** other than after an OP_Transaction */
TriggerStack *trigStack;
};
@@ -711,7 +705,6 @@ struct Trigger {
TriggerStep *step_list; /* Link list of trigger program steps */
char *strings; /* pointer to allocation of Token strings */
Trigger *pNext; /* Next trigger associated with the table */
- int isCommit; /* Set to TRUE once the trigger has been committed */
};
/*
@@ -847,9 +840,13 @@ void sqliteExprDelete(Expr*);
ExprList *sqliteExprListAppend(ExprList*,Expr*,Token*);
void sqliteExprListDelete(ExprList*);
void sqlitePragma(Parse*,Token*,Token*,int);
-void sqliteCommitInternalChanges(sqlite*);
+void sqliteResetInternalSchema(sqlite*);
+int sqliteInit(sqlite*, char**);
+void sqliteBeginParse(Parse*,int);
void sqliteRollbackInternalChanges(sqlite*);
+void sqliteCommitInternalChanges(sqlite*);
Table *sqliteResultSetOfSelect(Parse*,char*,Select*);
+void sqliteOpenMasterTable(Vdbe *v, int);
void sqliteStartTable(Parse*,Token*,Token*,int);
void sqliteAddColumn(Parse*,Token*);
void sqliteAddNotNull(Parse*, int);
@@ -929,7 +926,7 @@ void sqliteRegisterBuiltinFunctions(sqlite*);
int sqliteSafetyOn(sqlite*);
int sqliteSafetyOff(sqlite*);
int sqliteSafetyCheck(sqlite*);
-void sqliteChangeCookie(sqlite *);
+void sqliteChangeCookie(sqlite*, Vdbe*);
void sqliteCreateTrigger(Parse*, Token*, int, int, IdList*, Token*,
int, Expr*, TriggerStep*, char const*,int);
void sqliteDropTrigger(Parse*, Token*, int);
diff --git a/src/trigger.c b/src/trigger.c
index 95e8fc9f81..53eff0c41d 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -70,6 +70,12 @@ void sqliteCreateTrigger(
pParse->nErr++;
goto trigger_cleanup;
}
+ if( sqliteStrICmp(tab->zName, TEMP_MASTER_NAME)==0 ){
+ sqliteSetString(&pParse->zErrMsg, "cannot create trigger on system "
+ "table: " TEMP_MASTER_NAME, 0);
+ pParse->nErr++;
+ goto trigger_cleanup;
+ }
if( tab->pSelect && tr_tm != TK_INSTEAD ){
sqliteSetNString(&pParse->zErrMsg, "cannot create ", -1,
(tr_tm == TK_BEFORE)?"BEFORE":"AFTER", -1, " trigger on view: ", -1
@@ -100,7 +106,6 @@ void sqliteCreateTrigger(
nt->pColumns = pColumns;
nt->foreach = foreach;
nt->step_list = pStepList;
- nt->isCommit = 0;
offset = (int)(nt->strings - zData);
sqliteExprMoveStrings(nt->pWhen, offset);
@@ -119,20 +124,16 @@ void sqliteCreateTrigger(
/* if we are not initializing, and this trigger is not on a TEMP table,
** build the sqlite_master entry
*/
- if( !pParse->initFlag && !tab->isTemp ){
+ if( !pParse->initFlag ){
static VdbeOp insertTrig[] = {
- { OP_OpenWrite, 0, 2, MASTER_NAME},
{ OP_NewRecno, 0, 0, 0 },
{ OP_String, 0, 0, "trigger" },
- { OP_String, 0, 0, 0 }, /* 3: trigger name */
- { OP_String, 0, 0, 0 }, /* 4: table name */
+ { OP_String, 0, 0, 0 }, /* 2: trigger name */
+ { OP_String, 0, 0, 0 }, /* 3: table name */
{ OP_Integer, 0, 0, 0 },
- { OP_String, 0, 0, 0 }, /* 6: SQL */
+ { OP_String, 0, 0, 0 }, /* 5: SQL */
{ OP_MakeRecord, 5, 0, 0 },
{ OP_PutIntKey, 0, 0, 0 },
- { OP_Integer, 0, 0, 0 }, /* 9: Next cookie */
- { OP_SetCookie, 0, 0, 0 },
- { OP_Close, 0, 0, 0 },
};
int addr;
Vdbe *v;
@@ -141,12 +142,17 @@ void sqliteCreateTrigger(
v = sqliteGetVdbe(pParse);
if( v==0 ) goto trigger_cleanup;
sqliteBeginWriteOperation(pParse, 0);
+ sqliteOpenMasterTable(v, tab->isTemp);
addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
- sqliteVdbeChangeP3(v, addr+3, nt->name, 0);
- sqliteVdbeChangeP3(v, addr+4, nt->table, 0);
- sqliteVdbeChangeP3(v, addr+6, nt->strings, 0);
- sqliteChangeCookie(pParse->db);
- sqliteVdbeChangeP1(v, addr+9, pParse->db->next_cookie);
+ sqliteVdbeChangeP3(v, addr, tab->isTemp ? TEMP_MASTER_NAME : MASTER_NAME,
+ P3_STATIC);
+ sqliteVdbeChangeP3(v, addr+2, nt->name, 0);
+ sqliteVdbeChangeP3(v, addr+3, nt->table, 0);
+ sqliteVdbeChangeP3(v, addr+5, nt->strings, 0);
+ if( !tab->isTemp ){
+ sqliteChangeCookie(pParse->db, v);
+ }
+ sqliteVdbeAddOp(v, OP_Close, 0, 0);
sqliteEndWriteOperation(pParse);
}
@@ -312,11 +318,11 @@ void sqliteDeleteTrigger(Trigger *pTrigger){
* table. This is so that the trigger can be restored into the database schema
* if the transaction is rolled back.
*/
-void sqliteDropTrigger(Parse *pParse, Token *pName, int nested)
-{
+void sqliteDropTrigger(Parse *pParse, Token *pName, int nested){
char *zName;
Trigger *pTrigger;
Table *pTable;
+ Vdbe *v;
zName = sqliteStrNDup(pName->z, pName->n);
@@ -330,12 +336,9 @@ void sqliteDropTrigger(Parse *pParse, Token *pName, int nested)
}
/*
- * If this is not an "explain", do the following:
- * 1. Remove the trigger from its associated table structure
- * 2. Move the trigger from the trigHash hash to trigDrop
+ * If this is not an "explain", then delete the trigger structure.
*/
if( !pParse->explain ){
- /* 1 */
pTable = sqliteFindTable(pParse->db, pTrigger->table);
assert(pTable);
if( pTable->pTrigger == pTrigger ){
@@ -351,46 +354,34 @@ void sqliteDropTrigger(Parse *pParse, Token *pName, int nested)
}
assert(cc);
}
-
- /* 2 */
- sqliteHashInsert(&(pParse->db->trigHash), zName,
- pName->n + 1, NULL);
- sqliteHashInsert(&(pParse->db->trigDrop), pTrigger->name,
- pName->n + 1, pTrigger);
+ sqliteHashInsert(&(pParse->db->trigHash), zName, pName->n + 1, NULL);
+ sqliteDeleteTrigger(pTrigger);
}
- /* Unless this is a trigger on a TEMP TABLE, generate code to destroy the
- * database record of the trigger */
- if( !pTable->isTemp ){
+ /* Generate code to destroy the database record of the trigger.
+ */
+ if( pTable!=0 && !nested && (v = sqliteGetVdbe(pParse))!=0 ){
int base;
static VdbeOp dropTrigger[] = {
- { OP_OpenWrite, 0, 2, MASTER_NAME},
- { OP_Rewind, 0, ADDR(9), 0},
- { OP_String, 0, 0, 0}, /* 2 */
+ { OP_Rewind, 0, ADDR(8), 0},
+ { OP_String, 0, 0, 0}, /* 1 */
{ OP_MemStore, 1, 1, 0},
- { OP_MemLoad, 1, 0, 0}, /* 4 */
+ { OP_MemLoad, 1, 0, 0}, /* 3 */
{ OP_Column, 0, 1, 0},
- { OP_Ne, 0, ADDR(8), 0},
+ { OP_Ne, 0, ADDR(7), 0},
{ OP_Delete, 0, 0, 0},
- { OP_Next, 0, ADDR(4), 0}, /* 8 */
- { OP_Integer, 0, 0, 0}, /* 9 */
- { OP_SetCookie, 0, 0, 0},
- { OP_Close, 0, 0, 0},
+ { OP_Next, 0, ADDR(3), 0}, /* 7 */
};
- if( !nested ){
- sqliteBeginWriteOperation(pParse, 0);
- }
- base = sqliteVdbeAddOpList(pParse->pVdbe,
- ArraySize(dropTrigger), dropTrigger);
- sqliteVdbeChangeP3(pParse->pVdbe, base+2, zName, 0);
- if( !nested ){
- sqliteChangeCookie(pParse->db);
- }
- sqliteVdbeChangeP1(pParse->pVdbe, base+9, pParse->db->next_cookie);
- if( !nested ){
- sqliteEndWriteOperation(pParse);
+ sqliteBeginWriteOperation(pParse, 0);
+ sqliteOpenMasterTable(v, pTable->isTemp);
+ base = sqliteVdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger);
+ sqliteVdbeChangeP3(v, base+1, zName, 0);
+ if( !pTable->isTemp ){
+ sqliteChangeCookie(pParse->db, v);
}
+ sqliteVdbeAddOp(v, OP_Close, 0, 0);
+ sqliteEndWriteOperation(pParse);
}
sqliteFree(zName);
diff --git a/src/vdbe.c b/src/vdbe.c
index 74978b2414..a706669b6b 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -30,7 +30,7 @@
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
-** $Id: vdbe.c,v 1.158 2002/06/21 23:01:50 drh Exp $
+** $Id: vdbe.c,v 1.159 2002/06/25 01:09:12 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -3021,7 +3021,6 @@ case OP_Open: {
case OP_OpenAux: wrFlag = 0; pX = db->pBeTemp; break;
case OP_OpenWrAux: wrFlag = 1; pX = db->pBeTemp; break;
}
- assert( pX!=0 );
if( p2<=0 ){
if( tos<0 ) goto not_enough_stack;
Integerify(p, tos);
@@ -3047,6 +3046,7 @@ case OP_Open: {
cleanupCursor(&p->aCsr[i]);
memset(&p->aCsr[i], 0, sizeof(Cursor));
p->aCsr[i].nullRow = 1;
+ if( pX==0 ) break;
do{
rc = sqliteBtreeCursor(pX, p2, wrFlag, &p->aCsr[i].pCursor);
switch( rc ){
diff --git a/src/where.c b/src/where.c
index d0eb170ab7..9a90f6269d 100644
--- a/src/where.c
+++ b/src/where.c
@@ -13,7 +13,7 @@
** the WHERE clause of SQL statements. Also found here are subroutines
** to generate VDBE code to evaluate expressions.
**
-** $Id: where.c,v 1.55 2002/06/24 22:01:59 drh Exp $
+** $Id: where.c,v 1.56 2002/06/25 01:09:12 drh Exp $
*/
#include "sqliteInt.h"
@@ -479,7 +479,6 @@ WhereInfo *sqliteWhereBegin(
int inMask = 0; /* Index columns covered by an x IN .. term */
int nEq, m, score;
- if( pIdx->isDropped ) continue; /* Ignore dropped indices */
if( pIdx->nColumn>32 ) continue; /* Ignore indices too many columns */
for(j=0; j
"
}
+chng {2002 Jun 24 (2.5.2)} {
+Added the new SQLITE_TEMP_MASTER table which records the schema
+ for temporary tables in the same way that SQLITE_MASTER does for
+ persistent tables.
+Added an optimization to UNION ALL
+Fixed a bug in the processing of LEFT OUTER JOIN
+The LIMIT clause now works on subselects
+ORDER BY works on subselects
+There is a new TypeOf() function used to determine if an expression
+ is numeric or text.
+Autoincrement now works for INSERT from a SELECT.
+}
+
chng {2002 Jun 19 (2.5.1)} {
The query optimizer now attempts to implement the ORDER BY clause
using an index. Sorting is still used if not suitable index is
diff --git a/www/faq.tcl b/www/faq.tcl
index 98024bfe98..68e332b716 100644
--- a/www/faq.tcl
+++ b/www/faq.tcl
@@ -1,7 +1,7 @@
#
# Run this script to generated a faq.html output file
#
-set rcsid {$Id: faq.tcl,v 1.10 2002/04/25 00:21:50 drh Exp $}
+set rcsid {$Id: faq.tcl,v 1.11 2002/06/25 01:09:13 drh Exp $}
puts {
@@ -276,8 +276,19 @@ ORDER BY name;
using UPDATE, INSERT, or DELETE. The table is automatically updated by
CREATE TABLE, CREATE INDEX, DROP TABLE, and DROP INDEX commands.
- Temporary tables do not appear in the SQLITE_MASTER table. At this time
- there is no way to get a listing of temporary tables and indices.
+ Temporary tables do not appear in the SQLITE_MASTER table. Temporary
+ tables and their indices and triggers occur in another special table
+ named SQLITE_TEMP_MASTER. SQLITE_TEMP_MASTER works just like SQLITE_MASTER
+ except that it is only visible to the application that created the
+ temporary tables. To get a list of all tables, both permanent and
+ temporary, one can use a command similar to the following:
+
+SELECT name FROM
+ (SELECT * FROM sqlite_master UNION ALL
+ SELECT * FROM sqlite_temp_master)
+WHERE type='table'
+ORDER BY name
+
}
faq {
diff --git a/www/lang.tcl b/www/lang.tcl
index 35bb60721b..f5d3099c95 100644
--- a/www/lang.tcl
+++ b/www/lang.tcl
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
-set rcsid {$Id: lang.tcl,v 1.40 2002/06/12 22:33:54 drh Exp $}
+set rcsid {$Id: lang.tcl,v 1.41 2002/06/25 01:09:13 drh Exp $}
puts {
@@ -236,7 +236,9 @@ See the section titled
The exact text
of each CREATE INDEX statement is stored in the sqlite_master
-table. Everytime the database is opened, all CREATE INDEX statements
+or sqlite_temp_master table, depending on whether the table
+being indexed is temporary. Everytime the database is opened,
+all CREATE INDEX statements
are read from the sqlite_master table and used to regenerate
SQLite's internal representation of the index layout.
}
@@ -275,8 +277,8 @@ puts {
A CREATE TABLE statement is basically the keywords "CREATE TABLE"
followed by the name of a new table and a parenthesized list of column
definitions and constraints. The table name can be either an identifier
-or a string. The only reserved table name is "sqlite_master" which
-is the name of the table that records the database schema.
+or a string. Tables names that begin with "sqlite_" are reserved
+for use by the engine.
Each column definition is the name of the column followed by the
datatype for that column, then one or more optional column constraints.
@@ -343,6 +345,8 @@ SQLite's internal representation of the table layout.
If the original command was a CREATE TABLE AS then then an equivalent
CREATE TABLE statement is synthesized and store in sqlite_master
in place of the original command.
+The text of CREATE TEMPORARY TABLE statements are stored in the
+sqlite_temp_master table.
}
Section {CREATE TRIGGER} createtrigger
diff --git a/www/sqlite.tcl b/www/sqlite.tcl
index 5d4fb06acc..4954180441 100644
--- a/www/sqlite.tcl
+++ b/www/sqlite.tcl
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
-set rcsid {$Id: sqlite.tcl,v 1.16 2001/11/24 13:23:05 drh Exp $}
+set rcsid {$Id: sqlite.tcl,v 1.17 2002/06/25 01:09:13 drh Exp $}
puts {
@@ -117,6 +117,14 @@ indices from the database. You can not make manual changes
to the sqlite_master table.
+
+The schema for TEMPORARY tables is not stored in the "sqlite_master" table
+since TEMPORARY tables are not visible to applications other than the
+application that created the table. The schema for TEMPORARY tables
+is stored in another special table named "sqlite_temp_master". The
+"sqlite_temp_master" table is temporary itself.
+
+
Special commands to sqlite
@@ -335,8 +343,8 @@ puts {
executing the following query:
-SELECT name FROM sqlite_master
-WHERE type='table'
+SELECT name FROM sqlite_master WHERE type='table'
+UNION ALL SELECT name FROM sqlite_temp_master WHERE type='table'
ORDER BY name;
@@ -376,7 +384,9 @@ puts {
list mode, then entering the following query:
-SELECT sql FROM sqlite_master
+SELECT sql FROM
+ (SELECT * FROM sqlite_master UNION ALL
+ SELECT * FROM sqlite_temp_master)
WHERE type!='meta'
ORDER BY tbl_name, type DESC, name
@@ -385,7 +395,9 @@ ORDER BY tbl_name, type DESC, name
want the schema for a single table, the query looks like this:
-SELECT sql FROM sqlite_master
+SELECT sql FROM
+ (SELECT * FROM sqlite_master UNION ALL
+ SELECT * FROM sqlite_temp_master)
WHERE tbl_name LIKE '%s' AND type!='meta'
ORDER BY type DESC, name