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 {