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

    $desc

" } +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