From f57b339988d5caa23a2482c6c4f47a3c537efd58 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 8 Oct 2001 13:22:32 +0000 Subject: [PATCH] Support for temporary tables added. Still need more testing. (CVS 279) FossilOrigin-Name: 9368c62e4097aae3081a325962c1dec167fd253d --- manifest | 41 +++--- manifest.uuid | 2 +- src/build.c | 295 ++++++++++++++++++++++++++++++-------------- src/delete.c | 13 +- src/insert.c | 8 +- src/main.c | 28 +++-- src/os.c | 3 + src/pager.c | 12 +- src/parse.y | 9 +- src/sqliteInt.h | 12 +- src/tokenize.c | 4 +- src/update.c | 8 +- src/vdbe.c | 169 +++++++++++++++---------- src/vdbe.h | 194 +++++++++++++++-------------- src/where.c | 12 +- test/temptable.test | 124 +++++++++++++++++++ www/changes.tcl | 2 + www/lang.tcl | 59 +++++++-- 18 files changed, 684 insertions(+), 311 deletions(-) create mode 100644 test/temptable.test diff --git a/manifest b/manifest index ba3a348a84..c99767764f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Adding\stable\scolumn\squery\scapability\sto\ssupport\sODBC.\s(CVS\s278) -D 2001-10-06T16:33:02 +C Support\sfor\stemporary\stables\sadded.\s\sStill\sneed\smore\stesting.\s(CVS\s279) +D 2001-10-08T13:22:32 F Makefile.in 98d4627cb364537e4c3a29ee806171f3abf5211a F Makefile.template 1e54087c0390c4ce0bb5be43e14ba028283751e6 F README 93d2977cc5c6595c448de16bdefc312b9d401533 @@ -21,37 +21,37 @@ F publish.sh 502b907fa9e0214309406fa5f520b3d3c14f9c1d F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6 F src/btree.c 7e9c33a714ed1630562f89ad19847f5f28bd6d4d F src/btree.h 57d653ef5137b91f2a068aaf71a2905468dd2cb7 -F src/build.c 7cbac6c3a5d35e56f8d57bb6b07fba5e8a41c806 -F src/delete.c 81002d889aae874decf507627207c5d1b3599dc2 +F src/build.c 55ca22cd7af59b7f8895c601ed9cf006adf50f05 +F src/delete.c 93c9d5e160395020a25d59371625db74c97c7c4d F src/expr.c 2f68829d983ec3f92eeb8b89ce4b9e5704169a80 F src/hash.c bf36fb4cba114015123b0050f137d2c4553778a1 F src/hash.h 5f6e7c04c46ed015ab4e01797c2049b4af5b006d -F src/insert.c 01dd6ddee901a8a55c9b05a62b30023ec43de9ce -F src/main.c 6db4ba7ed8f1c1866b04eab6d0f9b46455e152c8 +F src/insert.c a48ba850461b203fb8dbc7add83fc6b6a9cf47f3 +F src/main.c 87b2fca50cbe8b400e1443b2c73693e18d9911cb F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c -F src/os.c 45376582c41dc8829330816d56b8e9e6cd1b7972 +F src/os.c d3f435d89241e06d4230b6f79a4e9d49102eb0a4 F src/os.h 0f478e2fef5ec1612f94b59b163d4807d4c77d6d -F src/pager.c 592c16b06ad07c715240e382028e29b0e83378be +F src/pager.c 3445bd7c18cbfdffd8d6d1077f0b2bdf788da4fe F src/pager.h a0d4c5ae271914aa07b62aee0707997d6932b6ca -F src/parse.y fcd3452640cf5ae2304ce430668112e0da07745a +F src/parse.y e88f1efe096a1a01c9076099fe1d81deedfa11de F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9 F src/random.c 708a23f69f40d6f2ae5ce1a04e6a4055d4a6ecec F src/select.c 0ef8ca1b7de2467fe082bcb35a5ab3b5be56153c F src/shell.c cb8c41f1b2173efd212dab3f35f1fc6bf32ead76 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in b95c161abf1d58bceb05290fa3f657d8f388fc11 -F src/sqliteInt.h 78b1890c4b61d35f33941cf9a2aae366b32b3dd3 +F src/sqliteInt.h d75506e003b508d8e2501217648f045496813f2c F src/table.c abd0adbe0fee39d995287b3bcccd908d174dfcac F src/tclsqlite.c 765599686c19ed777ac379928d732c8bfc63ebac F src/test1.c e4b31f62ea71963cbae44338acf477a04fc8fc49 F src/test2.c 0168b39225b768cfdadd534406f9dec58c27879e F src/test3.c 4a0d7b882fdae731dbb759f512ad867122452f96 -F src/tokenize.c 8a2aa0443f8bb1a02d021604377e2783e8e45f42 -F src/update.c 0449af173b5f2f0b26e2f0e4545ee0e0429763cb +F src/tokenize.c 5bd2dd048d77f4c683f0551a73d2fa5e964b53f0 +F src/update.c 49a1edb1a3e44dfff3f799e00f2a3319f2393cd8 F src/util.c 4da3be37d0fd3c640d2d3033503768afdc8e5387 -F src/vdbe.c 5c865988f8b33dcb8c5282f24bde404ab5d96899 -F src/vdbe.h c543a58f52fb654c90dd31d0d0c31309f4d838de -F src/where.c cce952b6a2459ac2296e3432876a4252d2fe3b87 +F src/vdbe.c 469c36ce2ef72a10447796dc5b5d61317e47fff2 +F src/vdbe.h 7eb7e9e6c58fe9430efab35e168f96cb4bd6cb45 +F src/where.c b676765ad0360769173b09f46265ddec8d48367a F test/all.test a2320eb40b462f25bd3e33115b1cabf3791450dd F test/bigrow.test a35f2de9948b24e427fb292c35947795efe182d0 F test/btree.test 47952c7a0c22660566264c68c0664592b7da85ce @@ -83,6 +83,7 @@ F test/subselect.test 335d3dad8d585726c447dfee8d9c4f7383c76b78 F test/table.test 3ef4254d62ece31a3872ab11cdaec846f6fa8fd1 F test/tableapi.test 162840153191a91a7dce6395f2334f9aef713b37 F test/tclsqlite.test a57bb478d7e9f0b2c927f92e161f391e2896631a +F test/temptable.test 99611832cdef52a30e62b091eaf941dbc934f303 F test/tester.tcl c7ddeebc14cc841abb37134cd5d40c1e3ad367c1 F test/trans.test 855337b8a178c73c433fcf8ee88e4b2f5efff0d9 F test/unique.test ef1f67607a7109e9c0842cd8557550fb121d7ec6 @@ -100,19 +101,19 @@ F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4 F www/arch.tcl 03b521d252575f93b9c52f7c8b0007011512fcfb F www/c_interface.tcl 8e8d9e66e8467c5751116c3427296bde77f474a6 -F www/changes.tcl 8c2ecc905283cfe3221722d8e8bcb660af2297b2 +F www/changes.tcl b42f68ebc6a590ab3dd4f16e389faad2a7f2d541 F www/crosscompile.tcl c99efacb3aefaa550c6e80d91b240f55eb9fd33e F www/download.tcl 3e51c9ff1326b0a182846134987301310dff7d60 F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c F www/index.tcl 68c815d64b35b2dcc4d4f6845827df71c6869f9f -F www/lang.tcl 33a74d727615ccbee8be7c8efd5876ce008c4b0e +F www/lang.tcl 3a7900e3f80cab50f322d925e573bd9f0acd57e1 F www/mingw.tcl fc5f4ba9d336b6e8c97347cc6496d6162461ef60 F www/opcode.tcl 4365ad9798872491dbd7d3071510ebe461785ac3 F www/speed.tcl ab7d6d3bc898472bd94320a5d3c63de928d4804b F www/sqlite.tcl 6a21242a272e9c0939a04419a51c3d50cae33e3e F www/tclsqlite.tcl 13d50723f583888fc80ae1a38247c0ab415066fa F www/vdbe.tcl bb7d620995f0a987293e9d4fb6185a3b077e9b44 -P e4980849403a8d7bd63753c9b7f275519bd7df4f -R 7502c066a19df2a451414aa8e23b0cac +P b63b3f3684a3d584ef99f54cde76b6c483bbfef7 +R 9a90e6e0cf1a1b0f9063fbb657761a98 U drh -Z 47fec5ca7cd29a261f31f63e809cee33 +Z 4e9556749f24d361b6f8c2df79fd7c4c diff --git a/manifest.uuid b/manifest.uuid index 586cbaeb4e..2afa3e0dce 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b63b3f3684a3d584ef99f54cde76b6c483bbfef7 \ No newline at end of file +9368c62e4097aae3081a325962c1dec167fd253d \ No newline at end of file diff --git a/src/build.c b/src/build.c index e8d213b43b..ef59bb03c1 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.44 2001/10/06 16:33:03 drh Exp $ +** $Id: build.c,v 1.45 2001/10/08 13:22:32 drh Exp $ */ #include "sqliteInt.h" #include @@ -65,7 +65,9 @@ void sqliteExec(Parse *pParse){ } /* -** Construct a new expression node and return a pointer to it. +** Construct a new expression node and return a pointer to it. Memory +** for this node is obtained from sqliteMalloc(). The calling function +** is responsible for making sure the node eventually gets freed. */ Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){ Expr *pNew; @@ -131,8 +133,8 @@ void sqliteExprDelete(Expr *p){ } /* -** Locate the in-memory structure that describes the -** format of a particular database table given the name +** Locate the in-memory structure that describes +** a particular database table given the name ** of that table. Return NULL if not found. */ Table *sqliteFindTable(sqlite *db, char *zName){ @@ -141,9 +143,9 @@ Table *sqliteFindTable(sqlite *db, char *zName){ } /* -** Locate the in-memory structure that describes the -** format of a particular index given the name -** of that index. Return NULL if not found. +** Locate the in-memory structure that describes +** a particular index given the name of that index. +** Return NULL if not found. */ Index *sqliteFindIndex(sqlite *db, char *zName){ Index *p = sqliteHashFind(&db->idxHash, zName, strlen(zName)+1); @@ -155,7 +157,7 @@ Index *sqliteFindIndex(sqlite *db, char *zName){ ** its memory structures. ** ** The index is removed from the database hash table if db!=NULL. -** But it is not unlinked from the Table that is being indexed. +** But the index is not unlinked from the Table that it indexes. ** Unlinking from the Table must be done by the calling function. */ static void sqliteDeleteIndex(sqlite *db, Index *pIndex){ @@ -167,7 +169,7 @@ static void sqliteDeleteIndex(sqlite *db, Index *pIndex){ /* ** Unlink the given index from its table, then remove -** the index from the index hash table, and free its memory +** the index from the index hash table and free its memory ** structures. */ static void sqliteUnlinkAndDeleteIndex(sqlite *db, Index *pIndex){ @@ -276,7 +278,7 @@ void sqliteCommitInternalChanges(sqlite *db){ /* ** This routine runs when one or more CREATE TABLE, CREATE INDEX, -** DROP TABLE, or DROP INDEX statements get rolled back. The +** 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. ** @@ -334,14 +336,19 @@ char *sqliteTableNameFromToken(Token *pName){ ** 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. +** 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 +** CREATE and TABLE. ** -** The new table is constructed in fields of the pParse structure. As -** more of the CREATE TABLE statement is parsed, additional action -** routines are called to build up more of the table. +** The new table record is initialized and put in pParse->pNewTable. +** As more of the CREATE TABLE statement is parsed, additional action +** routines will be called to add more information to this record. +** At the end of the CREATE TABLE statement, the sqliteEndTable() routine +** is called to complete the construction of the new table record. */ -void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ +void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName, int isTemp){ Table *pTable; + Index *pIdx; char *zName; sqlite *db = pParse->db; Vdbe *v; @@ -349,15 +356,54 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ pParse->sFirstToken = *pStart; zName = sqliteTableNameFromToken(pName); if( zName==0 ) return; + + /* Before trying to create a temporary table, make sure the Btree for + ** holding temporary tables is open. + */ + 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 " + "file for storing temporary tables", 0); + pParse->nErr++; + return; + } + if( db->flags & SQLITE_InTrans ){ + rc = sqliteBtreeBeginTrans(db->pBeTemp); + if( rc!=SQLITE_OK ){ + sqliteSetNString(&pParse->zErrMsg, "unable to get a write lock on " + "the temporary datbase file", 0); + pParse->nErr++; + return; + } + } + } + + /* Make sure the new table name does not collide with an existing + ** index or table name. Issue an error message if it does. + ** + ** If we are re-reading the sqlite_master table because of a schema + ** change and a new permanent table is found whose name collides with + ** an existing temporary table, then ignore the new permanent table. + ** We will continue parsing, but the pParse->nameClash flag will be set + ** so we will know to discard the table record once parsing has finished. + */ pTable = sqliteFindTable(db, zName); if( pTable!=0 ){ - sqliteSetNString(&pParse->zErrMsg, "table ", 0, pName->z, pName->n, - " already exists", 0, 0); - sqliteFree(zName); - pParse->nErr++; - return; + if( pTable->isTemp && pParse->initFlag ){ + pParse->nameClash = 1; + }else{ + sqliteSetNString(&pParse->zErrMsg, "table ", 0, pName->z, pName->n, + " already exists", 0, 0); + sqliteFree(zName); + pParse->nErr++; + return; + } + }else{ + pParse->nameClash = 0; } - if( sqliteFindIndex(db, zName) ){ + if( (pIdx = sqliteFindIndex(db, zName))!=0 && + (!pIdx->pTable->isTemp || !pParse->initFlag) ){ sqliteSetString(&pParse->zErrMsg, "there is already an index named ", zName, 0); sqliteFree(zName); @@ -370,6 +416,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ pTable->nCol = 0; pTable->aCol = 0; pTable->pIndex = 0; + pTable->isTemp = isTemp; if( pParse->pNewTable ) sqliteDeleteTable(db, pParse->pNewTable); pParse->pNewTable = pTable; if( !pParse->initFlag && (v = sqliteGetVdbe(pParse))!=0 ){ @@ -378,7 +425,9 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); pParse->schemaVerified = 1; } - sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2, MASTER_NAME, 0); + if( !isTemp ){ + sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2, MASTER_NAME, 0); + } } } @@ -418,7 +467,7 @@ void sqliteAddNotNull(Parse *pParse){ int i; if( (p = pParse->pNewTable)==0 ) return; i = p->nCol-1; - p->aCol[i].notNull = 1; + if( i>=0 ) p->aCol[i].notNull = 1; } /* @@ -437,10 +486,12 @@ void sqliteAddColumnType(Parse *pParse, Token *pFirst, Token *pLast){ char *z, **pz; if( (p = pParse->pNewTable)==0 ) return; i = p->nCol-1; + if( i<0 ) return; pz = &p->aCol[i].zType; n = pLast->n + ((int)pLast->z) - (int)pFirst->z; sqliteSetNString(pz, pFirst->z, n, 0); z = *pz; + if( z==0 ) return; for(i=j=0; z[i]; i++){ int c = z[i]; if( isspace(c) ) continue; @@ -463,6 +514,7 @@ void sqliteAddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){ char **pz; if( (p = pParse->pNewTable)==0 ) return; i = p->nCol-1; + if( i<0 ) return; pz = &p->aCol[i].zDflt; if( minusFlag ){ sqliteSetNString(pz, "-", 1, pVal->z, pVal->n, 0); @@ -500,13 +552,16 @@ static void changeCookie(sqlite *db){ ** This routine is called to report the final ")" that terminates ** a CREATE TABLE statement. ** -** The table structure is added to the internal hash tables. +** The table structure that other action routines have been building +** is added to the internal hash tables, assuming no errors have +** occurred. ** ** An entry for the table is made in the master table on disk, -** unless initFlag==1. When initFlag==1, it means we are reading -** the master table because we just connected to the database, so -** the entry for this table already exists in the master table. -** We do not want to create it again. +** unless this is a temporary table or initFlag==1. When initFlag==1, +** it means we are reading the sqlite_master table because we just +** connected to the database or because the sqlite_master table has +** recently changes, so the entry for this table already exists in +** the sqlite_master table. We do not want to create it again. */ void sqliteEndTable(Parse *pParse, Token *pEnd){ Table *p; @@ -516,9 +571,10 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){ p = pParse->pNewTable; if( p==0 ) return; - /* Add the table to the in-memory representation of the database + /* Add the table to the in-memory representation of the database. */ - if( pParse->explain==0 ){ + assert( pParse->nameClash==0 || pParse->initFlag==0 ); + if( pParse->explain==0 && pParse->nameClash==0 ){ sqliteHashInsert(&db->tblHash, p->zName, strlen(p->zName)+1, p); pParse->pNewTable = 0; db->nTable++; @@ -537,6 +593,9 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){ /* If not initializing, then create a record for the new table ** in the SQLITE_MASTER table of the database. + ** + ** If this is a TEMPORARY table, then just create the table. Do not + ** make an entry in SQLITE_MASTER. */ if( !pParse->initFlag ){ int n, addr; @@ -545,20 +604,24 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){ v = sqliteGetVdbe(pParse); if( v==0 ) return; n = (int)pEnd->z - (int)pParse->sFirstToken.z + 1; - sqliteVdbeAddOp(v, OP_NewRecno, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_String, 0, 0, "table", 0); - sqliteVdbeAddOp(v, OP_String, 0, 0, p->zName, 0); - sqliteVdbeAddOp(v, OP_String, 0, 0, p->zName, 0); + if( !p->isTemp ){ + sqliteVdbeAddOp(v, OP_NewRecno, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_String, 0, 0, "table", 0); + sqliteVdbeAddOp(v, OP_String, 0, 0, p->zName, 0); + sqliteVdbeAddOp(v, OP_String, 0, 0, p->zName, 0); + } addr = sqliteVdbeAddOp(v, OP_CreateTable, 0, 0, 0, 0); sqliteVdbeChangeP3(v, addr, (char *)&p->tnum, -1); p->tnum = 0; - addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0); - sqliteVdbeChangeP3(v, addr, pParse->sFirstToken.z, n); - sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0); - changeCookie(db); - sqliteVdbeAddOp(v, OP_SetCookie, db->next_cookie, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + if( !p->isTemp ){ + addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0); + sqliteVdbeChangeP3(v, addr, pParse->sFirstToken.z, n); + sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0); + changeCookie(db); + sqliteVdbeAddOp(v, OP_SetCookie, db->next_cookie, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + } if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); } @@ -619,8 +682,7 @@ void sqliteDropTable(Parse *pParse, Token *pName){ { OP_Ne, 0, ADDR(3), 0}, { OP_Delete, 0, 0, 0}, { OP_Goto, 0, ADDR(3), 0}, - { OP_Destroy, 0, 0, 0}, /* 9 */ - { OP_SetCookie, 0, 0, 0}, /* 10 */ + { OP_SetCookie, 0, 0, 0}, /* 9 */ { OP_Close, 0, 0, 0}, }; Index *pIdx; @@ -629,13 +691,15 @@ void sqliteDropTable(Parse *pParse, Token *pName){ sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); pParse->schemaVerified = 1; } - base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); - sqliteVdbeChangeP3(v, base+2, pTable->zName, 0); - sqliteVdbeChangeP1(v, base+9, pTable->tnum); - changeCookie(db); - sqliteVdbeChangeP1(v, base+10, db->next_cookie); + if( !pTable->isTemp ){ + base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); + sqliteVdbeChangeP3(v, base+2, pTable->zName, 0); + changeCookie(db); + sqliteVdbeChangeP1(v, base+9, db->next_cookie); + } + sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->isTemp, 0, 0); for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pTable->isTemp, 0, 0); } if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); @@ -679,8 +743,9 @@ void sqliteCreateIndex( Index *pIndex; /* The index to be created */ char *zName = 0; int i, j; - Token nullId; /* Fake token for an empty ID list */ + Token nullId; /* Fake token for an empty ID list */ sqlite *db = pParse->db; + int hideName = 0; /* Do not put table name in the hash table */ if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index; @@ -702,26 +767,56 @@ void sqliteCreateIndex( goto exit_create_index; } + /* If this index is created while re-reading the schema from sqlite_master + ** but the table associated with this index is a temporary table, it can + ** only mean that the table this index is really associated with is one + ** whose name is hidden behind a temporary table with the same name. + ** Since its table has been suppressed, we need to also suppress the + ** index. + */ + if( pParse->initFlag && pTab->isTemp ){ + goto exit_create_index; + } + /* ** Find the name of the index. Make sure there is not already another - ** index or table with the same name. If pName==0 it means that we are + ** index or table with the same name. + ** + ** Exception: If we are reading the names of permanent indices from the + ** sqlite_master table (because some other process changed the schema) and + ** one of the index names collides with the name of a temporary table or + ** index, then we will continue to process this index, but we will not + ** store its name in the hash table. Set the hideName flag to accomplish + ** this. + ** + ** If pName==0 it means that we are ** dealing with a primary key or UNIQUE constraint. We have to invent our ** own name. */ if( pName ){ + Index *pISameName; /* Another index with the same name */ + Table *pTSameName; /* A table with same name as the index */ zName = sqliteTableNameFromToken(pName); if( zName==0 ) goto exit_create_index; - if( sqliteFindIndex(db, zName) ){ - sqliteSetString(&pParse->zErrMsg, "index ", zName, - " already exists", 0); - pParse->nErr++; - goto exit_create_index; + if( (pISameName = sqliteFindIndex(db, zName))!=0 ){ + if( pISameName->pTable->isTemp && pParse->initFlag ){ + hideName = 1; + }else{ + sqliteSetString(&pParse->zErrMsg, "index ", zName, + " already exists", 0); + pParse->nErr++; + goto exit_create_index; + } } - if( sqliteFindTable(db, zName) ){ - sqliteSetString(&pParse->zErrMsg, "there is already a table named ", - zName, 0); - pParse->nErr++; - goto exit_create_index; + if( (pTSameName = sqliteFindTable(db, zName))!=0 ){ + if( pTSameName->isTemp && pParse->initFlag ){ + hideName = 1; + }else{ + sqliteSetString(&pParse->zErrMsg, "there is already a table named ", + zName, 0); + pParse->nErr++; + goto exit_create_index; + } } }else{ char zBuf[30]; @@ -781,7 +876,7 @@ void sqliteCreateIndex( */ pIndex->pNext = pTab->pIndex; pTab->pIndex = pIndex; - if( !pParse->explain ){ + if( !pParse->explain && !hideName ){ sqliteHashInsert(&db->idxHash, pIndex->zName, strlen(zName)+1, pIndex); db->flags |= SQLITE_InternChanges; } @@ -815,6 +910,7 @@ void sqliteCreateIndex( int lbl1, lbl2; int i; int addr; + int isTemp = pTab->isTemp; v = sqliteGetVdbe(pParse); if( v==0 ) goto exit_create_index; @@ -824,28 +920,39 @@ void sqliteCreateIndex( sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); pParse->schemaVerified = 1; } - sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2, MASTER_NAME, 0); + if( !isTemp ){ + sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2, MASTER_NAME, 0); + } } - sqliteVdbeAddOp(v, OP_NewRecno, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_String, 0, 0, "index", 0); - sqliteVdbeAddOp(v, OP_String, 0, 0, pIndex->zName, 0); - sqliteVdbeAddOp(v, OP_String, 0, 0, pTab->zName, 0); - addr = sqliteVdbeAddOp(v, OP_CreateIndex, 0, 0, 0, 0); + if( !isTemp ){ + sqliteVdbeAddOp(v, OP_NewRecno, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_String, 0, 0, "index", 0); + sqliteVdbeAddOp(v, OP_String, 0, 0, pIndex->zName, 0); + sqliteVdbeAddOp(v, OP_String, 0, 0, pTab->zName, 0); + } + addr = sqliteVdbeAddOp(v, OP_CreateIndex, 0, isTemp, 0, 0); sqliteVdbeChangeP3(v, addr, (char*)&pIndex->tnum, -1); pIndex->tnum = 0; if( pTable ){ - sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0, 0, 0); + if( isTemp ){ + sqliteVdbeAddOp(v, OP_OpenWrAux, 1, 0, 0, 0); + }else{ + sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0, 0, 0); + } } - addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0); - if( pStart && pEnd ){ - n = (int)pEnd->z - (int)pStart->z + 1; - sqliteVdbeChangeP3(v, addr, pStart->z, n); + if( !isTemp ){ + addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0); + if( pStart && pEnd ){ + n = (int)pEnd->z - (int)pStart->z + 1; + sqliteVdbeChangeP3(v, addr, pStart->z, n); + } + sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0); } - sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0); if( pTable ){ - sqliteVdbeAddOp(v, OP_Open, 2, pTab->tnum, pTab->zName, 0); + sqliteVdbeAddOp(v, isTemp ? OP_OpenAux : OP_Open, + 2, pTab->tnum, pTab->zName, 0); lbl1 = sqliteVdbeMakeLabel(v); lbl2 = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Rewind, 2, 0, 0, 0); @@ -859,12 +966,14 @@ void sqliteCreateIndex( sqliteVdbeAddOp(v, OP_Goto, 0, lbl1, 0, 0); sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, lbl2); sqliteVdbeAddOp(v, OP_Close, 2, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Close, 1, 0, 0, 0); } - sqliteVdbeAddOp(v, OP_Close, 1, 0, 0, 0); if( pTable!=0 ){ - changeCookie(db); - sqliteVdbeAddOp(v, OP_SetCookie, db->next_cookie, 0, 0, 0); - sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + if( !isTemp ){ + changeCookie(db); + sqliteVdbeAddOp(v, OP_SetCookie, db->next_cookie, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0); + } if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); } @@ -916,17 +1025,20 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ { OP_Close, 0, 0, 0}, }; int base; + Table *pTab = pIndex->pTable; if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); pParse->schemaVerified = 1; } - base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); - sqliteVdbeChangeP3(v, base+2, pIndex->zName, 0); - sqliteVdbeChangeP1(v, base+8, pIndex->tnum); - changeCookie(db); - sqliteVdbeChangeP1(v, base+9, db->next_cookie); + if( !pTab->isTemp ){ + base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); + sqliteVdbeChangeP3(v, base+2, pIndex->zName, 0); + changeCookie(db); + sqliteVdbeChangeP1(v, base+9, db->next_cookie); + } + sqliteVdbeAddOp(v, OP_Destroy, pIndex->tnum, pTab->isTemp, 0, 0); if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Commit, 0, 0, 0, 0); } @@ -1091,6 +1203,7 @@ void sqliteCopy( } v = sqliteGetVdbe(pParse); if( v ){ + int openOp; if( (db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0, 0, 0); sqliteVdbeAddOp(v, OP_VerifyCookie, db->schema_cookie, 0, 0, 0); @@ -1099,9 +1212,10 @@ void sqliteCopy( addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0, 0, 0); sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n); sqliteVdbeDequoteP3(v, addr); - sqliteVdbeAddOp(v, OP_OpenWrite, 0, pTab->tnum, pTab->zName, 0); + openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; + sqliteVdbeAddOp(v, openOp, 0, pTab->tnum, pTab->zName, 0); for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - sqliteVdbeAddOp(v, OP_OpenWrite, i, pIdx->tnum, pIdx->zName, 0); + sqliteVdbeAddOp(v, openOp, i, pIdx->tnum, pIdx->zName, 0); } end = sqliteVdbeMakeLabel(v); addr = sqliteVdbeAddOp(v, OP_FileRead, pTab->nCol, end, 0, 0); @@ -1374,6 +1488,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ }else #endif - if( zLeft ) sqliteFree(zLeft); - if( zRight ) sqliteFree(zRight); + {} + sqliteFree(zLeft); + sqliteFree(zRight); } diff --git a/src/delete.c b/src/delete.c index 120295b5f3..6b02eb59a8 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** -** $Id: delete.c,v 1.15 2001/09/23 02:35:53 drh Exp $ +** $Id: delete.c,v 1.16 2001/10/08 13:22:32 drh Exp $ */ #include "sqliteInt.h" @@ -91,9 +91,9 @@ void sqliteDeleteFrom( ** It is easier just to erase the whole table. */ if( pWhere==0 ){ - sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->isTemp, 0, 0); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, 0, 0, 0); + sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pTab->isTemp, 0, 0); } } @@ -101,6 +101,8 @@ void sqliteDeleteFrom( ** the table an pick which records to delete. */ else{ + int openOp; + /* Begin the database scan */ sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0); @@ -121,9 +123,10 @@ void sqliteDeleteFrom( */ base = pParse->nTab; sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); - sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum, 0, 0); + openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; + sqliteVdbeAddOp(v, openOp, base, pTab->tnum, 0, 0); for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, OP_OpenWrite, base+i, pIdx->tnum, 0, 0); + sqliteVdbeAddOp(v, openOp, base+i, pIdx->tnum, 0, 0); } end = sqliteVdbeMakeLabel(v); addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0); diff --git a/src/insert.c b/src/insert.c index 9529a1f9ff..711f8eb940 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.21 2001/10/06 16:33:03 drh Exp $ +** $Id: insert.c,v 1.22 2001/10/08 13:22:33 drh Exp $ */ #include "sqliteInt.h" @@ -48,6 +48,7 @@ void sqliteInsert( int base; /* First available cursor */ int iCont, iBreak; /* Beginning and end of the loop over srcTab */ sqlite *db; /* The main database structure */ + int openOp; /* Opcode used to open cursors */ if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup; db = pParse->db; @@ -155,9 +156,10 @@ void sqliteInsert( ** all indices of that table. */ base = pParse->nTab; - sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum, pTab->zName, 0); + openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; + sqliteVdbeAddOp(v, openOp, base, pTab->tnum, pTab->zName, 0); for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ - sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum, pIdx->zName, 0); + sqliteVdbeAddOp(v, openOp, idx+base, pIdx->tnum, pIdx->zName, 0); } /* If the data source is a SELECT statement, then we have to create diff --git a/src/main.c b/src/main.c index d96aad914f..184c49ff70 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.43 2001/10/06 16:33:03 drh Exp $ +** $Id: main.c,v 1.44 2001/10/08 13:22:33 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -309,7 +309,9 @@ no_mem_on_open: } /* -** Erase all schema information from the schema hash table. +** Erase all schema information from the schema hash table. Except +** tables that are created using CREATE TEMPORARY TABLE are preserved +** if the preserverTemps 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. @@ -317,15 +319,24 @@ no_mem_on_open: ** 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){ +static void clearHashTable(sqlite *db, int preserveTemps){ HashElem *pElem; Hash temp1; temp1 = db->tblHash; sqliteHashInit(&db->tblHash, SQLITE_HASH_STRING, 0); sqliteHashClear(&db->idxHash); for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ - Table *pTbl = sqliteHashData(pElem); - sqliteDeleteTable(db, pTbl); + Table *pTab = sqliteHashData(pElem); + if( preserveTemps && pTab->isTemp ){ + Index *pIdx; + sqliteHashInsert(&db->tblHash, pTab->zName, strlen(pTab->zName)+1, pTab); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + int n = strlen(pIdx->zName)+1; + sqliteHashInsert(&db->idxHash, pIdx->zName, n, pIdx); + } + }else{ + sqliteDeleteTable(db, pTab); + } } sqliteHashClear(&temp1); db->flags &= ~SQLITE_Initialized; @@ -336,7 +347,10 @@ static void clearHashTable(sqlite *db){ */ void sqlite_close(sqlite *db){ sqliteBtreeClose(db->pBe); - clearHashTable(db); + clearHashTable(db, 0); + if( db->pBeTemp ){ + sqliteBtreeClose(db->pBeTemp); + } sqliteFree(db); } @@ -429,7 +443,7 @@ int sqlite_exec( } sqliteStrRealloc(pzErrMsg); if( sParse.rc==SQLITE_SCHEMA ){ - clearHashTable(db); + clearHashTable(db, 1); } return sParse.rc; } diff --git a/src/os.c b/src/os.c index 0c0f4b357b..e1d0ebc6c4 100644 --- a/src/os.c +++ b/src/os.c @@ -397,6 +397,9 @@ int sqliteOsLock(OsFile id, int wrlock){ lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0L; rc = fcntl(id, F_SETLK, &lock); + if( rc ){ + fcntl(id, F_GETLK, &lock); /* For debugging */ + } return rc==0 ? SQLITE_OK : SQLITE_BUSY; #endif #if OS_WIN diff --git a/src/pager.c b/src/pager.c index 6bb71efc7b..4584ffdb22 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.25 2001/10/06 16:33:03 drh Exp $ +** @(#) $Id: pager.c,v 1.26 2001/10/08 13:22:33 drh Exp $ */ #include "sqliteInt.h" #include "pager.h" @@ -640,12 +640,16 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ /* If a journal file exists, try to play it back. */ if( sqliteOsFileExists(pPager->zJournal) ){ - int rc; + int rc, dummy; /* Open the journal for exclusive access. Return SQLITE_BUSY if - ** we cannot get exclusive access to the journal file + ** we cannot get exclusive access to the journal file. + ** + ** Even though we will only be reading from the journal, not writing, + ** we have to open the journal for writing in order to obtain an + ** exclusive access lock. */ - rc = sqliteOsOpenReadOnly(pPager->zJournal, &pPager->jfd); + rc = sqliteOsOpenReadWrite(pPager->zJournal, &pPager->jfd, &dummy); if( rc==SQLITE_OK ){ pPager->journalOpen = 1; } diff --git a/src/parse.y b/src/parse.y index 56e6af75af..a190751e08 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.34 2001/10/06 16:33:03 drh Exp $ +** @(#) $Id: parse.y,v 1.35 2001/10/08 13:22:33 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -62,7 +62,11 @@ cmd ::= ROLLBACK trans_opt. {sqliteRollbackTransaction(pParse);} ///////////////////// The CREATE TABLE statement //////////////////////////// // cmd ::= create_table create_table_args. -create_table ::= CREATE(X) TABLE ids(Y). {sqliteStartTable(pParse,&X,&Y);} +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;} create_table_args ::= LP columnlist conslist_opt RP(X). {sqliteEndTable(pParse,&X);} columnlist ::= columnlist COMMA column. @@ -91,6 +95,7 @@ id(A) ::= END(X). {A = X;} id(A) ::= PRAGMA(X). {A = X;} id(A) ::= CLUSTER(X). {A = X;} id(A) ::= ID(X). {A = X;} +id(A) ::= TEMP(X). {A = X;} // And "ids" is an identifer-or-string. // diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 59f0a25577..90c1869c82 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.57 2001/10/06 16:33:03 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.58 2001/10/08 13:22:33 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -139,6 +139,7 @@ typedef struct AggExpr AggExpr; */ struct sqlite { Btree *pBe; /* The B*Tree backend */ + Btree *pBeTemp; /* Backend for session temporary tables */ int flags; /* Miscellanous flags. See below */ int file_format; /* What file format version is this database? */ int schema_cookie; /* Magic number that changes with the schema */ @@ -195,6 +196,7 @@ struct Table { 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 isDelete; /* True if this table is being deleted */ + u8 isTemp; /* True if stored in db->pBeTemp instead of db->pBe */ }; /* @@ -355,7 +357,9 @@ struct AggExpr { }; /* -** An SQL parser context +** An SQL parser context. A copy of this structure is passed through +** the parser and down into all the parser action routine in order to +** carry around information that is global to the entire parse. */ struct Parse { sqlite *db; /* The main database structure */ @@ -372,8 +376,8 @@ struct Parse { 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 */ int newTnum; /* Table number to use when reparsing CREATE TABLEs */ - int newKnum; /* Primary key number when reparsing CREATE TABLEs */ int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated cursors */ int nMem; /* Number of memory cells used so far */ @@ -423,7 +427,7 @@ void sqliteExprListDelete(ExprList*); void sqlitePragma(Parse*,Token*,Token*,int); void sqliteCommitInternalChanges(sqlite*); void sqliteRollbackInternalChanges(sqlite*); -void sqliteStartTable(Parse*,Token*,Token*); +void sqliteStartTable(Parse*,Token*,Token*,int); void sqliteAddColumn(Parse*,Token*); void sqliteAddNotNull(Parse*); void sqliteAddColumnType(Parse*,Token*,Token*); diff --git a/src/tokenize.c b/src/tokenize.c index 619ae6ea90..0ad771150e 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -15,7 +15,7 @@ ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** -** $Id: tokenize.c,v 1.24 2001/10/06 16:33:03 drh Exp $ +** $Id: tokenize.c,v 1.25 2001/10/08 13:22:33 drh Exp $ */ #include "sqliteInt.h" #include @@ -84,6 +84,8 @@ static Keyword aKeywordTable[] = { { "SELECT", 0, TK_SELECT, 0 }, { "SET", 0, TK_SET, 0 }, { "TABLE", 0, TK_TABLE, 0 }, + { "TEMP", 0, TK_TEMP, 0 }, + { "TEMPORARY", 0, TK_TEMP, 0 }, { "TRANSACTION", 0, TK_TRANSACTION, 0 }, { "UNION", 0, TK_UNION, 0 }, { "UNIQUE", 0, TK_UNIQUE, 0 }, diff --git a/src/update.c b/src/update.c index 164a360c08..db23bb6e8a 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.16 2001/09/27 03:22:34 drh Exp $ +** $Id: update.c,v 1.17 2001/10/08 13:22:33 drh Exp $ */ #include "sqliteInt.h" @@ -39,6 +39,7 @@ void sqliteUpdate( int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ + int openOp; /* Opcode used to open tables */ if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup; db = pParse->db; @@ -159,9 +160,10 @@ void sqliteUpdate( */ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0); base = pParse->nTab; - sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum, 0, 0); + openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; + sqliteVdbeAddOp(v, openOp, base, pTab->tnum, 0, 0); for(i=0; itnum, 0, 0); + sqliteVdbeAddOp(v, openOp, base+i+1, apIdx[i]->tnum, 0, 0); } /* Loop over every record that needs updating. We have to load diff --git a/src/vdbe.c b/src/vdbe.c index 0e2614bcb4..63adee7612 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.80 2001/10/06 16:33:03 drh Exp $ +** $Id: vdbe.c,v 1.81 2001/10/08 13:22:33 drh Exp $ */ #include "sqliteInt.h" #include @@ -793,30 +793,31 @@ void sqliteVdbeDelete(Vdbe *p){ static char *zOpName[] = { 0, "Transaction", "Commit", "Rollback", "ReadCookie", "SetCookie", "VerifyCookie", "Open", "OpenTemp", - "OpenWrite", "Close", "MoveTo", "Fcnt", - "NewRecno", "Put", "Distinct", "Found", - "NotFound", "Delete", "Column", "KeyAsData", - "Recno", "FullKey", "Rewind", "Next", - "Destroy", "Clear", "CreateIndex", "CreateTable", - "Reorganize", "BeginIdx", "NextIdx", "PutIdx", - "DeleteIdx", "MemLoad", "MemStore", "ListOpen", - "ListWrite", "ListRewind", "ListRead", "ListClose", - "SortOpen", "SortPut", "SortMakeRec", "SortMakeKey", - "Sort", "SortNext", "SortKey", "SortCallback", - "SortClose", "FileOpen", "FileRead", "FileColumn", - "FileClose", "AggReset", "AggFocus", "AggIncr", - "AggNext", "AggSet", "AggGet", "SetInsert", - "SetFound", "SetNotFound", "SetClear", "MakeRecord", - "MakeKey", "MakeIdxKey", "Goto", "If", - "Halt", "ColumnCount", "ColumnName", "Callback", - "Integer", "String", "Null", "Pop", - "Dup", "Pull", "Add", "AddImm", - "Subtract", "Multiply", "Divide", "Min", - "Max", "Like", "Glob", "Eq", - "Ne", "Lt", "Le", "Gt", - "Ge", "IsNull", "NotNull", "Negative", - "And", "Or", "Not", "Concat", - "Noop", "Strlen", "Substr", + "OpenWrite", "OpenAux", "OpenWrAux", "Close", + "MoveTo", "Fcnt", "NewRecno", "Put", + "Distinct", "Found", "NotFound", "Delete", + "Column", "KeyAsData", "Recno", "FullKey", + "Rewind", "Next", "Destroy", "Clear", + "CreateIndex", "CreateTable", "Reorganize", "BeginIdx", + "NextIdx", "PutIdx", "DeleteIdx", "MemLoad", + "MemStore", "ListOpen", "ListWrite", "ListRewind", + "ListRead", "ListClose", "SortOpen", "SortPut", + "SortMakeRec", "SortMakeKey", "Sort", "SortNext", + "SortKey", "SortCallback", "SortClose", "FileOpen", + "FileRead", "FileColumn", "FileClose", "AggReset", + "AggFocus", "AggIncr", "AggNext", "AggSet", + "AggGet", "SetInsert", "SetFound", "SetNotFound", + "SetClear", "MakeRecord", "MakeKey", "MakeIdxKey", + "Goto", "If", "Halt", "ColumnCount", + "ColumnName", "Callback", "Integer", "String", + "Null", "Pop", "Dup", "Pull", + "Add", "AddImm", "Subtract", "Multiply", + "Divide", "Min", "Max", "Like", + "Glob", "Eq", "Ne", "Lt", + "Le", "Gt", "Ge", "IsNull", + "NotNull", "Negative", "And", "Or", + "Not", "Concat", "Noop", "Strlen", + "Substr", }; /* @@ -1920,6 +1921,12 @@ case OP_MakeIdxKey: { */ case OP_Transaction: { int busy = 0; + if( db->pBeTemp ){ + rc = sqliteBtreeBeginTrans(db->pBeTemp); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } + } do{ rc = sqliteBtreeBeginTrans(pBt); switch( rc ){ @@ -1951,7 +1958,9 @@ case OP_Transaction: { ** A read lock continues to be held if there are still cursors open. */ case OP_Commit: { - rc = sqliteBtreeCommit(pBt); + if( db->pBeTemp==0 || (rc = sqliteBtreeCommit(db->pBeTemp))==SQLITE_OK ){ + rc = sqliteBtreeCommit(pBt); + } if( rc==SQLITE_OK ){ sqliteCommitInternalChanges(db); }else{ @@ -1971,6 +1980,9 @@ case OP_Commit: { ** the read and write locks on the database. */ case OP_Rollback: { + if( db->pBeTemp ){ + sqliteBtreeRollback(db->pBeTemp); + } rc = sqliteBtreeRollback(pBt); sqliteRollbackInternalChanges(db); break; @@ -2065,6 +2077,16 @@ case OP_VerifyCookie: { ** The P3 value is not actually used by this opcode and may be ** omitted. But the code generator usually inserts the index or ** table name into P3 to make the code easier to read. +** +** See also OpenAux and OpenWrite. +*/ +/* Opcode: OpenAux P1 P2 P3 +** +** Open a read-only cursor in the auxiliary table set. This opcode +** works exactly like OP_Open except that it opens the cursor on the +** auxiliary table set (the file used to store tables created using +** CREATE TEMPORARY TABLE) instead of in the main database file. +** See OP_Open for additional information. */ /* Opcode: OpenWrite P1 P2 P3 ** @@ -2074,13 +2096,33 @@ case OP_VerifyCookie: { ** This instruction works just like Open except that it opens the cursor ** in read/write mode. For a given table, there can be one or more read-only ** cursors or a single read/write cursor but not both. +** +** See also OpWrAux. */ +/* Opcode: OpenWrAux P1 P2 P3 +** +** Open a read/write cursor in the auxiliary table set. This opcode works +** just like OpenWrite except that the auxiliary table set (the file used +** to store tables created using CREATE TEMPORARY TABLE) is used in place +** of the main database file. +*/ +case OP_OpenAux: +case OP_OpenWrAux: case OP_OpenWrite: case OP_Open: { int busy = 0; int i = pOp->p1; int tos = p->tos; int p2 = pOp->p2; + int wrFlag; + Btree *pX; + switch( pOp->opcode ){ + case OP_Open: wrFlag = 0; pX = pBt; break; + case OP_OpenWrite: wrFlag = 1; pX = pBt; break; + 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); @@ -2105,8 +2147,7 @@ case OP_Open: { cleanupCursor(&p->aCsr[i]); memset(&p->aCsr[i], 0, sizeof(Cursor)); do{ - int wrFlag = pOp->opcode==OP_OpenWrite; - rc = sqliteBtreeCursor(pBt, p2, wrFlag, &p->aCsr[i].pCursor); + rc = sqliteBtreeCursor(pX, p2, wrFlag, &p->aCsr[i].pCursor); switch( rc ){ case SQLITE_BUSY: { if( xBusy==0 || (*xBusy)(pBusyArg, pOp->p3, ++busy)==0 ){ @@ -2133,6 +2174,13 @@ case OP_Open: { ** file. The temporary file is opened read/write even if the main ** database is read-only. The temporary file is deleted when the ** cursor is closed. +** +** This opcode is used for tables that exist for the duration of a single +** SQL statement only. Tables created using CREATE TEMPORARY TABLE +** are opened using OP_OpenAux or OP_OpenWrAux. "Temporary" in the +** context of this opcode means for the duration of a single SQL statement +** whereas "Temporary" in the context of CREATE TABLE means for the duration +** of the connection to the database. Same word; different meanings. */ case OP_OpenTemp: { int i = pOp->p1; @@ -2765,34 +2813,43 @@ case OP_DeleteIdx: { break; } -/* Opcode: Destroy P1 * * +/* Opcode: Destroy P1 P2 * ** ** Delete an entire database table or index whose root page in the database ** file is given by P1. ** +** The table being destroyed is in the main database file if P2==0. If +** P2==1 then the table to be clear is in the auxiliary database file +** that is used to store tables create using CREATE TEMPORARY TABLE. +** ** See also: Clear */ case OP_Destroy: { - sqliteBtreeDropTable(pBt, pOp->p1); + sqliteBtreeDropTable(pOp->p2 ? db->pBeTemp : pBt, pOp->p1); break; } -/* Opcode: Clear P1 * * +/* Opcode: Clear P1 P2 * ** ** Delete all contents of the database table or index whose root page ** in the database file is given by P1. But, unlike Destroy, do not ** remove the table or index from the database file. ** +** The table being clear is in the main database file if P2==0. If +** P2==1 then the table to be clear is in the auxiliary database file +** that is used to store tables create using CREATE TEMPORARY TABLE. +** ** See also: Destroy */ case OP_Clear: { - sqliteBtreeClearTable(pBt, pOp->p1); + sqliteBtreeClearTable(pOp->p2 ? db->pBeTemp : pBt, pOp->p1); break; } -/* Opcode: CreateTable * * P3 +/* Opcode: CreateTable * P2 P3 ** -** Allocate a new table in the main database file. Push the page number +** Allocate a new table in the main database file if P2==0 or in the +** auxiliary database file if P2==1. Push the page number ** for the root page of the new table onto the stack. ** ** The root page number is also written to a memory location that P3 @@ -2802,39 +2859,20 @@ case OP_Clear: { ** ** See also: CreateIndex */ +/* Opcode: CreateIndex * P2 P3 +** +** This instruction does exactly the same thing as CreateTable. It +** has a different name for historical reasons. +** +** See also: CreateTable +*/ +case OP_CreateIndex: case OP_CreateTable: { int i = ++p->tos; int pgno; VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) assert( pOp->p3!=0 && pOp->p3dyn==0 ); - rc = sqliteBtreeCreateTable(pBt, &pgno); - if( rc==SQLITE_OK ){ - aStack[i].i = pgno; - aStack[i].flags = STK_Int; - *(u32*)pOp->p3 = pgno; - pOp->p3 = 0; - } - break; -} - -/* Opcode: CreateIndex * * P3 -** -** Allocate a new Index in the main database file. Push the page number -** for the root page of the new table onto the stack. -** -** The root page number is also written to a memory location that P3 -** points to. This is the mechanism is used to write the root page -** number into the parser's internal data structures that describe the -** new index. -** -** See also: CreateTable -*/ -case OP_CreateIndex: { - int i = ++p->tos; - int pgno; - VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) - assert( pOp->p3!=0 && pOp->p3dyn==0 ); - rc = sqliteBtreeCreateTable(pBt, &pgno); + rc = sqliteBtreeCreateTable(pOp->p2 ? db->pBeTemp : pBt, &pgno); if( rc==SQLITE_OK ){ aStack[i].i = pgno; aStack[i].flags = STK_Int; @@ -3164,6 +3202,7 @@ case OP_SortNext: { break; } +#if 0 /* NOT USED */ /* Opcode: SortKey P1 * * ** ** Push the key for the topmost element of the sorter onto the stack. @@ -3182,6 +3221,7 @@ case OP_SortKey: { } break; } +#endif /* NOT USED */ /* Opcode: SortCallback P1 P2 * ** @@ -3245,6 +3285,7 @@ case OP_FileOpen: { break; } +#if 0 /* NOT USED */ /* Opcode: FileClose * * * ** ** Close a file previously opened using FileOpen. This is a no-op @@ -3267,6 +3308,7 @@ case OP_FileClose: { p->nLineAlloc = 0; break; } +#endif /* Opcode: FileRead P1 P2 P3 ** @@ -3597,6 +3639,7 @@ case OP_AggNext: { break; } +#if 0 /* NOT USED */ /* Opcode: SetClear P1 * * ** ** Remove all elements from the P1-th Set. @@ -3608,6 +3651,7 @@ case OP_SetClear: { } break; } +#endif /* NOT USED */ /* Opcode: SetInsert P1 * P3 ** @@ -3874,6 +3918,7 @@ cleanup: if( rc!=SQLITE_OK ){ closeAllCursors(p); sqliteBtreeRollback(pBt); + if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp); sqliteRollbackInternalChanges(db); db->flags &= ~SQLITE_InTrans; } diff --git a/src/vdbe.h b/src/vdbe.h index 211d6ede5b..b1fde29cb2 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,7 +15,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.25 2001/09/27 15:11:55 drh Exp $ +** $Id: vdbe.h,v 1.26 2001/10/08 13:22:33 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -71,116 +71,118 @@ typedef struct VdbeOp VdbeOp; #define OP_Open 7 #define OP_OpenTemp 8 #define OP_OpenWrite 9 -#define OP_Close 10 -#define OP_MoveTo 11 -#define OP_Fcnt 12 -#define OP_NewRecno 13 -#define OP_Put 14 -#define OP_Distinct 15 -#define OP_Found 16 -#define OP_NotFound 17 -#define OP_Delete 18 -#define OP_Column 19 -#define OP_KeyAsData 20 -#define OP_Recno 21 -#define OP_FullKey 22 -#define OP_Rewind 23 -#define OP_Next 24 +#define OP_OpenAux 10 +#define OP_OpenWrAux 11 +#define OP_Close 12 +#define OP_MoveTo 13 +#define OP_Fcnt 14 +#define OP_NewRecno 15 +#define OP_Put 16 +#define OP_Distinct 17 +#define OP_Found 18 +#define OP_NotFound 19 +#define OP_Delete 20 +#define OP_Column 21 +#define OP_KeyAsData 22 +#define OP_Recno 23 +#define OP_FullKey 24 +#define OP_Rewind 25 +#define OP_Next 26 -#define OP_Destroy 25 -#define OP_Clear 26 -#define OP_CreateIndex 27 -#define OP_CreateTable 28 -#define OP_Reorganize 29 +#define OP_Destroy 27 +#define OP_Clear 28 +#define OP_CreateIndex 29 +#define OP_CreateTable 30 +#define OP_Reorganize 31 -#define OP_BeginIdx 30 -#define OP_NextIdx 31 -#define OP_PutIdx 32 -#define OP_DeleteIdx 33 +#define OP_BeginIdx 32 +#define OP_NextIdx 33 +#define OP_PutIdx 34 +#define OP_DeleteIdx 35 -#define OP_MemLoad 34 -#define OP_MemStore 35 +#define OP_MemLoad 36 +#define OP_MemStore 37 -#define OP_ListOpen 36 -#define OP_ListWrite 37 -#define OP_ListRewind 38 -#define OP_ListRead 39 -#define OP_ListClose 40 +#define OP_ListOpen 38 +#define OP_ListWrite 39 +#define OP_ListRewind 40 +#define OP_ListRead 41 +#define OP_ListClose 42 -#define OP_SortOpen 41 -#define OP_SortPut 42 -#define OP_SortMakeRec 43 -#define OP_SortMakeKey 44 -#define OP_Sort 45 -#define OP_SortNext 46 -#define OP_SortKey 47 -#define OP_SortCallback 48 -#define OP_SortClose 49 +#define OP_SortOpen 43 +#define OP_SortPut 44 +#define OP_SortMakeRec 45 +#define OP_SortMakeKey 46 +#define OP_Sort 47 +#define OP_SortNext 48 +#define OP_SortKey 49 +#define OP_SortCallback 50 +#define OP_SortClose 51 -#define OP_FileOpen 50 -#define OP_FileRead 51 -#define OP_FileColumn 52 -#define OP_FileClose 53 +#define OP_FileOpen 52 +#define OP_FileRead 53 +#define OP_FileColumn 54 +#define OP_FileClose 55 -#define OP_AggReset 54 -#define OP_AggFocus 55 -#define OP_AggIncr 56 -#define OP_AggNext 57 -#define OP_AggSet 58 -#define OP_AggGet 59 +#define OP_AggReset 56 +#define OP_AggFocus 57 +#define OP_AggIncr 58 +#define OP_AggNext 59 +#define OP_AggSet 60 +#define OP_AggGet 61 -#define OP_SetInsert 60 -#define OP_SetFound 61 -#define OP_SetNotFound 62 -#define OP_SetClear 63 +#define OP_SetInsert 62 +#define OP_SetFound 63 +#define OP_SetNotFound 64 +#define OP_SetClear 65 -#define OP_MakeRecord 64 -#define OP_MakeKey 65 -#define OP_MakeIdxKey 66 +#define OP_MakeRecord 66 +#define OP_MakeKey 67 +#define OP_MakeIdxKey 68 -#define OP_Goto 67 -#define OP_If 68 -#define OP_Halt 69 +#define OP_Goto 69 +#define OP_If 70 +#define OP_Halt 71 -#define OP_ColumnCount 70 -#define OP_ColumnName 71 -#define OP_Callback 72 +#define OP_ColumnCount 72 +#define OP_ColumnName 73 +#define OP_Callback 74 -#define OP_Integer 73 -#define OP_String 74 -#define OP_Null 75 -#define OP_Pop 76 -#define OP_Dup 77 -#define OP_Pull 78 +#define OP_Integer 75 +#define OP_String 76 +#define OP_Null 77 +#define OP_Pop 78 +#define OP_Dup 79 +#define OP_Pull 80 -#define OP_Add 79 -#define OP_AddImm 80 -#define OP_Subtract 81 -#define OP_Multiply 82 -#define OP_Divide 83 -#define OP_Min 84 -#define OP_Max 85 -#define OP_Like 86 -#define OP_Glob 87 -#define OP_Eq 88 -#define OP_Ne 89 -#define OP_Lt 90 -#define OP_Le 91 -#define OP_Gt 92 -#define OP_Ge 93 -#define OP_IsNull 94 -#define OP_NotNull 95 -#define OP_Negative 96 -#define OP_And 97 -#define OP_Or 98 -#define OP_Not 99 -#define OP_Concat 100 -#define OP_Noop 101 +#define OP_Add 81 +#define OP_AddImm 82 +#define OP_Subtract 83 +#define OP_Multiply 84 +#define OP_Divide 85 +#define OP_Min 86 +#define OP_Max 87 +#define OP_Like 88 +#define OP_Glob 89 +#define OP_Eq 90 +#define OP_Ne 91 +#define OP_Lt 92 +#define OP_Le 93 +#define OP_Gt 94 +#define OP_Ge 95 +#define OP_IsNull 96 +#define OP_NotNull 97 +#define OP_Negative 98 +#define OP_And 99 +#define OP_Or 100 +#define OP_Not 101 +#define OP_Concat 102 +#define OP_Noop 103 -#define OP_Strlen 102 -#define OP_Substr 103 +#define OP_Strlen 104 +#define OP_Substr 105 -#define OP_MAX 103 +#define OP_MAX 105 /* ** Prototypes for the VDBE interface. See comments on the implementation diff --git a/src/where.c b/src/where.c index 6105349dba..971e20a2cf 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.21 2001/09/18 02:02:23 drh Exp $ +** $Id: where.c,v 1.22 2001/10/08 13:22:33 drh Exp $ */ #include "sqliteInt.h" @@ -285,15 +285,19 @@ WhereInfo *sqliteWhereBegin( /* Open all tables in the pTabList and all indices in aIdx[]. */ for(i=0; inId; i++){ - sqliteVdbeAddOp(v, OP_Open, base+i, pTabList->a[i].pTab->tnum, - pTabList->a[i].pTab->zName, 0); + int openOp; + Table *pTab; + + pTab = pTabList->a[i].pTab; + openOp = pTab->isTemp ? OP_OpenAux : OP_Open; + sqliteVdbeAddOp(v, openOp, base+i, pTab->tnum, pTab->zName, 0); if( i==0 && !pParse->schemaVerified && (pParse->db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0, 0, 0); pParse->schemaVerified = 1; } if( inId+i, aIdx[i]->tnum, + sqliteVdbeAddOp(v, openOp, base+pTabList->nId+i, aIdx[i]->tnum, aIdx[i]->zName, 0); } } diff --git a/test/temptable.test b/test/temptable.test new file mode 100644 index 0000000000..211ba25658 --- /dev/null +++ b/test/temptable.test @@ -0,0 +1,124 @@ +# 2001 October 7 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests for temporary tables and indices. +# +# $Id: temptable.test,v 1.1 2001/10/08 13:22:33 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Create an alternative connection to the database +# +do_test temptable-1.0 { + sqlite db2 ./test.db +} {} + +# Create a permanent table. +# +do_test temptable-1.1 { + execsql {CREATE TABLE t1(a,b,c);} + execsql {INSERT INTO t1 VALUES(1,2,3);} + execsql {SELECT * FROM t1} +} {1 2 3} +do_test temptable-1.2 { + catch {db2 eval {SELECT * FROM sqlite_master}} + db2 eval {SELECT * FROM t1} +} {1 2 3} +do_test testtable-1.3 { + execsql {SELECT name FROM sqlite_master} +} {t1} +do_test testtable-1.4 { + db2 eval {SELECT name FROM sqlite_master} +} {t1} + +# Create a temporary table. Verify that only one of the two +# processes can see it. +# +do_test testtable-1.5 { + db2 eval { + CREATE TEMP TABLE t2(x,y,z); + INSERT INTO t2 VALUES(4,5,6); + } + db2 eval {SELECT * FROM t2} +} {4 5 6} +do_test testtable-1.6 { + catch {execsql {SELECT * FROM sqlite_master}} + catchsql {SELECT * FROM t2} +} {1 {no such table: t2}} +do_test testtable-1.7 { + catchsql {INSERT INTO t2 VALUES(8,9,0);} +} {1 {no such table: t2}} +do_test testtable-1.8 { + db2 eval {INSERT INTO t2 VALUES(8,9,0);} + db2 eval {SELECT * FROM t2 ORDER BY x} +} {4 5 6 8 9 0} +do_test testtable-1.9 { + db2 eval {DELETE FROM t2 WHERE x==8} + db2 eval {SELECT * FROM t2 ORDER BY x} +} {4 5 6} +do_test testtable-1.10 { + db2 eval {DELETE FROM t2} + db2 eval {SELECT * FROM t2} +} {} +do_test testtable-1.11 { + db2 eval { + INSERT INTO t2 VALUES(7,6,5); + INSERT INTO t2 VALUES(4,3,2); + SELECT * FROM t2 ORDER BY x; + } +} {4 3 2 7 6 5} +do_test testtable-1.12 { + db2 eval {DROP TABLE t2;} + set r [catch {db2 eval {SELECT * FROM t2}} msg] + lappend r $msg +} {1 {no such table: t2}} + +# Make sure temporary tables work with transactions +# +do_test testtable-2.1 { + execsql { + BEGIN TRANSACTION; + CREATE TEMPORARY TABLE t2(x,y); + INSERT INTO t2 VALUES(1,2); + SELECT * FROM t2; + } +} {1 2} +do_test testtable-2.2 { + execsql {ROLLBACK} + catchsql {SELECT * FROM t2} +} {1 {no such table: t2}} +do_test testtable-2.3 { + execsql { + BEGIN TRANSACTION; + CREATE TEMPORARY TABLE t2(x,y); + INSERT INTO t2 VALUES(1,2); + SELECT * FROM t2; + } +} {1 2} +do_test testtable-2.4 { + execsql {COMMIT} + catchsql {SELECT * FROM t2} +} {0 {1 2}} +do_test testtable-2.5 { + set r [catch {db2 eval {SELECT * FROM t2}} msg] + lappend r $msg +} {1 {no such table: t2}} + + +# Check for correct name collision processing. A name collision can +# occur when process A creates a temporary table T then process B +# creates a permanent table also named T. The temp table in process A +# hides the existance of the permanent table. +# + +finish_test diff --git a/www/changes.tcl b/www/changes.tcl index b92ee88f41..13013959fb 100644 --- a/www/changes.tcl +++ b/www/changes.tcl @@ -20,8 +20,10 @@ proc chng {date desc} { chng {2001 Oct ? (2.0.2)} {
  • Removed some unused "#include " that were causing problems for VC++.
  • +
  • Fixed sqlite.h so that it is usable from C++
  • Added the FULL_COLUMN_NAMES pragma. When set to "ON", the names of columns are reported back as TABLE.COLUMN instead of just COLUMN.
  • +
  • Added support for TEMPORARY tables and indices.
  • } chng {2001 Oct 2 (2.0.1)} { diff --git a/www/lang.tcl b/www/lang.tcl index 0c9c258323..2c1dbb70d1 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.10 2001/09/28 17:47:14 drh Exp $} +set rcsid {$Id: lang.tcl,v 1.11 2001/10/08 13:22:33 drh Exp $} puts { @@ -119,15 +119,14 @@ ROLLBACK [TRANSACTION []] puts {

    Beginning in version 2.0, SQLite supports transactions with rollback and atomic commit. However, only a single level of -transaction is allowed. In other words, transactions -may not be nested. +transaction is allowed. Transactions may not be nested.

    No changes can be made to the database except within a transaction. Any command that changes the database (basically, any SQL command other than SELECT) will automatically starts a transaction if -when is not already in effect. Automatically stared transactions +one is not already in effect. Automatically stared transactions are committed at the conclusion of the command.

    @@ -206,7 +205,7 @@ SQLite's internal representation of the index layout.

    Section {CREATE TABLE} {createtable} Syntax {sql-command} { -CREATE TABLE ( +CREATE [TEMP | TEMPORARY] TABLE ( [, ]* [, ]* ) @@ -246,6 +245,13 @@ The DEFAULT constraint specifies a default value to use when doing an INSERT.

    +

    If the "TEMP" or "TEMPORARY" keyword occurs in between "CREATE" +and "TABLE" then the table that is created is only visible to the +process that opened the database and is automatically deleted when +the database is closed. Any indices created on a temporary table +are also temporary. Temporary tables and indices are stored in a +separate file distinct from the main database file.

    +

    There are no arbitrary limits on the number of columns or on the number of constraints in a table. The total amount of data in a single row is limited to 65535 bytes.

    @@ -579,20 +585,55 @@ puts {

    The VACUUM command is an SQLite extension modelled after a similar command found in PostgreSQL. If VACUUM is invoked with the name of a table or index then it is suppose to clean up the named table or index. -In the current implementation, VACUUM is a no-op. +In version 1.0 of SQLite, the VACUUM command would invoke +gdbm_reorganize() to clean up the backend database file. +Beginning with version 2.0 of SQLite, GDBM is no longer used for +the database backend and VACUUM has become a no-op.

    } Section PRAGMA pragma Syntax {sql-statement} { -PRAGMA = +PRAGMA = | +PRAGMA () } puts {

    The PRAGMA command is used to modify the operation of the SQLite library. -Additional documentation on the PRAMGA statement is forthcoming. -

    +The pragma command is experimental and specific pragma statements may +removed or added in future releases of SQLite. Use this command +with caution.

    + +

    The current implementation supports the following pragmas:

    + +
      +
    • PRAGMA cache_size = Number-of-pages;

      +

      Change the maximum number of database disk pages that SQLite + will hold in memory at once. Each page uses about 1.5K of RAM. + The default cache size is 100. If you are doing UPDATEs or DELETEs + that change many rows of a database and you do not mind if SQLite + uses more memory, you can increase the cache size for a possible speed + improvement.

    • + +
    • PRAGMA full_column_names = ON; +
      PRAGMA full_column_names = OFF;

      +

      The column names reported in an SQLite callback are normally just + the name of the column itself, except for joins when "TABLE.COLUMN" + is used. But when full_column_names is turned on, column names are + always reported as "TABLE.COLUMN" even for simple queries.

    • + +
    • PRAGMA vdbe_trace = ON;
      PRAGMA vdbe_trace = OFF;

      +

      Turn tracing of the virtual database engine inside of the + SQLite library on and off. This is used for debugging.

    • + +
    • PRAGMA parser_trace = ON;
      PRAGMA parser_trace = OFF;

      +

      Turn tracing of the SQL parser inside of the + SQLite library on and off. This is used for debugging.

    • +
    + +

    No error message is generated if an unknown pragma is issued. +Unknown pragmas are ignored.

    } puts {