diff options
author | drh <drh@noemail.net> | 2003-03-27 12:51:24 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2003-03-27 12:51:24 +0000 |
commit | d24cc427b7f83ca65aefbb6cf1831d0461f16357 (patch) | |
tree | e8c0700ba474499cd46b5f6cc49c3bd1a4b607c0 | |
parent | 84e6335c0c244dfde752ee4e166b6770dd9d2e10 (diff) | |
download | sqlite-d24cc427b7f83ca65aefbb6cf1831d0461f16357.tar.gz sqlite-d24cc427b7f83ca65aefbb6cf1831d0461f16357.zip |
Changes to the "sqlite" structure that allow simultaneous operations on
multiple database files. Many regession tests pass - but not all of them.
Do not use this version except for debugging SQLite itself. (CVS 883)
FossilOrigin-Name: d2fb2bb50cf1e13feb90995079f291384abd6ba9
-rw-r--r-- | manifest | 36 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/build.c | 319 | ||||
-rw-r--r-- | src/delete.c | 32 | ||||
-rw-r--r-- | src/expr.c | 32 | ||||
-rw-r--r-- | src/func.c | 45 | ||||
-rw-r--r-- | src/insert.c | 20 | ||||
-rw-r--r-- | src/main.c | 34 | ||||
-rw-r--r-- | src/parse.y | 46 | ||||
-rw-r--r-- | src/select.c | 9 | ||||
-rw-r--r-- | src/sqliteInt.h | 59 | ||||
-rw-r--r-- | src/trigger.c | 152 | ||||
-rw-r--r-- | src/update.c | 16 | ||||
-rw-r--r-- | src/where.c | 6 |
14 files changed, 474 insertions, 334 deletions
@@ -1,5 +1,5 @@ -C Re-generated.\s(CVS\s882) -D 2003-03-24T09:42:16 +C Changes\sto\sthe\s"sqlite"\sstructure\sthat\sallow\ssimultaneous\soperations\son\nmultiple\sdatabase\sfiles.\s\sMany\sregession\stests\spass\s-\sbut\snot\sall\sof\sthem.\nDo\snot\suse\sthis\sversion\sexcept\sfor\sdebugging\sSQLite\sitself.\s(CVS\s883) +D 2003-03-27T12:51:24 F Makefile.in 6917c2149a586f11b47c428f2ba748eb1da04f69 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -22,28 +22,28 @@ F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2 F src/auth.c f37bfc9451b8c1fa52f34adff474560018892729 F src/btree.c 327819bb858d534072f5004973f8bcdd50f133d6 F src/btree.h 8209bfadf5845d4fdaa60f471bb360f894cd4095 -F src/build.c a965338bee81ce20fb0ce38419e9a9d159e720f0 -F src/delete.c 96a0ae021f960a7f2dbb3d1456802624deacfd3c +F src/build.c abd5da38923bba6c5dc14d0e24b8e05f68ff459b +F src/delete.c e1552bd5a26418c32b8516cb5602bf5473651c68 F src/encode.c faf03741efe921755ec371cf4a6984536de00042 -F src/expr.c 8af430cdbcb6122dd0320c8860602bd4cc778486 -F src/func.c 90c583f0b91220f7cd411a2407deaf9327245d63 +F src/expr.c eae205a27ec45232f234f281f8827c3be58b303d +F src/func.c 882c3ed5a02be18cd904715c7ec62947a34a3605 F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8 -F src/insert.c 1f31bdec48bd3915615e7251b74c27dcfaee9b88 -F src/main.c 66cd7ff4fc9f43719aaa2bc22db5babd8f437a9f +F src/insert.c 8d23a2d9995d5bee5b7eb44cb9dc354e0874e70a +F src/main.c e0d9a86541f0644be5c875c6d8f5062c325c653d F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565 F src/os.c dfed46091f69cd2d1e601f8a214d41344f2b00b6 F src/os.h aa52f0c9da321ff6134d19f2ca959e18e33615d0 F src/pager.c dd1dfa4d929a58b44175f3117360ff1553671173 F src/pager.h 97d9a8cc5103750efd8037d71ebfb41849ef2f2f -F src/parse.y 7a9f333e7b09b0584fb4cc3799f8828f612e282e +F src/parse.y 243cfc277d0e5ce87086c015e717dec98d6648a7 F src/printf.c fc5fdef6e92ad205005263661fe9716f55a49f3e F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe -F src/select.c 06ddc007c20862b3beb8c1c2504db664335d6706 +F src/select.c afdc06d4606d14ab5793ef480206def6b02a2f19 F src/shell.c 0d260a007e0668fc7dda2b0c89bd597ef2966ec6 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in 6f648803f2ffb9beb35cb1cfa42b323d55519171 -F src/sqliteInt.h ad95c947582d0584240ed413c5f1e9df71749ebe +F src/sqliteInt.h c4338bc3c75784840d51fd8a744a63e444ec496d F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63 F src/tclsqlite.c 8167d40fd34036701e07492d07a6f9e5c4015241 F src/test1.c 7ad4e6308dde0bf5a0f0775ce20cb2ec37a328f8 @@ -51,12 +51,12 @@ F src/test2.c 5014337d8576b731cce5b5a14bec4f0daf432700 F src/test3.c c12ea7f1c3fbbd58904e81e6cb10ad424e6fc728 F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e F src/tokenize.c 675b4718d17c69fe7609dc8e85e426ef002be811 -F src/trigger.c aafc83ea108ec6a1b501b31b7fb6cebcd4725fd1 -F src/update.c 5c644629cc73993ba762bf186944816581213bd1 +F src/trigger.c 578e9d07c5b0d07374f85d7f311126cf9c9d6bcf +F src/update.c 785e0e1c8df2043dc96ad7c298fb11aaa3ebc8af F src/util.c 73b668d1ed468df650dc00685a5e4ffa6887feb4 F src/vdbe.c 7171dbe873760f403b2501e96fd3d1bd852b3ce8 F src/vdbe.h ed43771f1dc2b994d5c484fdf2eab357c6ef0ee3 -F src/where.c 3111c1c209023e4f6b7b7eb0df48cef0010967c3 +F src/where.c e5733f7d5e9cc4ed3590dc3401f779e7b7bb8127 F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242 F test/auth.test 33e8b9680eb0ce521c54096fff1c9ab506c7dfb8 F test/bigfile.test 1cd8256d4619c39bea48147d344f348823e78678 @@ -156,7 +156,7 @@ F www/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P b1ca4e13dcab92cc532f4051f24bf5b1be251463 -R e880aa98002065d15c576e440b78c820 -U a.rottmann -Z f0092f1365d37d9048c83d0706e14c28 +P f0c5bcf72cf393ea4c5cd126d085cb959eebd5f2 +R 6683142d762d7951c57ba32402494ae2 +U drh +Z fa7baaef0faab26ced3760ebfbc99fb5 diff --git a/manifest.uuid b/manifest.uuid index 3d05260ab..d5c9b37a5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f0c5bcf72cf393ea4c5cd126d085cb959eebd5f2
\ No newline at end of file +d2fb2bb50cf1e13feb90995079f291384abd6ba9
\ No newline at end of file diff --git a/src/build.c b/src/build.c index ba6da93c3..4900e9231 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.133 2003/03/20 01:16:58 drh Exp $ +** $Id: build.c,v 1.134 2003/03/27 12:51:24 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -113,9 +113,14 @@ void sqliteExec(Parse *pParse){ ** a particular database table given the name ** of that table. Return NULL if not found. */ -Table *sqliteFindTable(sqlite *db, const char *zName){ - Table *p; - p = sqliteHashFind(&db->tblHash, zName, strlen(zName)+1); +Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){ + Table *p = 0; + int i; + for(i=0; i<db->nDb; i++){ + if( zDatabase!=0 && sqliteStrICmp(zDatabase, db->aDb[i].zName) ) continue; + p = sqliteHashFind(&db->aDb[i].tblHash, zName, strlen(zName)+1); + if( p ) break; + } return p; } @@ -124,9 +129,14 @@ Table *sqliteFindTable(sqlite *db, const char *zName){ ** a particular index given the name of that index. ** Return NULL if not found. */ -Index *sqliteFindIndex(sqlite *db, const char *zName){ - Index *p; - p = sqliteHashFind(&db->idxHash, zName, strlen(zName)+1); +Index *sqliteFindIndex(sqlite *db, const char *zName, const char *zDb){ + Index *p = 0; + int i; + for(i=0; i<db->nDb; i++){ + if( zDb && sqliteStrICmp(zDb, db->aDb[i].zName) ) continue; + p = sqliteHashFind(&db->aDb[i].idxHash, zName, strlen(zName)+1); + if( p ) break; + } return p; } @@ -140,10 +150,13 @@ Index *sqliteFindIndex(sqlite *db, const char *zName){ */ static void sqliteDeleteIndex(sqlite *db, Index *p){ Index *pOld; + assert( db!=0 && p->zName!=0 ); - pOld = sqliteHashInsert(&db->idxHash, p->zName, strlen(p->zName)+1, 0); + pOld = sqliteHashInsert(&db->aDb[p->iDb].idxHash, p->zName, + strlen(p->zName)+1, 0); if( pOld!=0 && pOld!=p ){ - sqliteHashInsert(&db->idxHash, pOld->zName, strlen(pOld->zName)+1, pOld); + sqliteHashInsert(&db->aDb[p->iDb].idxHash, pOld->zName, + strlen(pOld->zName)+1, pOld); } sqliteFree(p); } @@ -176,23 +189,27 @@ void sqliteResetInternalSchema(sqlite *db){ HashElem *pElem; Hash temp1; Hash temp2; + int i; - sqliteHashClear(&db->aFKey); - temp1 = db->tblHash; - temp2 = db->trigHash; - sqliteHashInit(&db->trigHash, SQLITE_HASH_STRING, 0); - sqliteHashClear(&db->idxHash); - for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){ - Trigger *pTrigger = sqliteHashData(pElem); - sqliteDeleteTrigger(pTrigger); - } - sqliteHashClear(&temp2); - sqliteHashInit(&db->tblHash, SQLITE_HASH_STRING, 0); - for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ - Table *pTab = sqliteHashData(pElem); - sqliteDeleteTable(db, pTab); - } - sqliteHashClear(&temp1); + for(i=0; i<db->nDb; i++){ + Db *pDb = &db->aDb[i]; + temp1 = pDb->tblHash; + temp2 = pDb->trigHash; + sqliteHashInit(&pDb->trigHash, SQLITE_HASH_STRING, 0); + sqliteHashClear(&pDb->aFKey); + sqliteHashClear(&pDb->idxHash); + for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){ + Trigger *pTrigger = sqliteHashData(pElem); + sqliteDeleteTrigger(pTrigger); + } + sqliteHashClear(&temp2); + sqliteHashInit(&pDb->tblHash, SQLITE_HASH_STRING, 0); + for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ + Table *pTab = sqliteHashData(pElem); + sqliteDeleteTable(db, pTab); + } + sqliteHashClear(&temp1); + } db->flags &= ~(SQLITE_Initialized|SQLITE_InternChanges); } @@ -241,6 +258,7 @@ void sqliteDeleteTable(sqlite *db, Table *pTable){ */ for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ pNext = pIndex->pNext; + assert( pIndex->iDb==pTable->iDb || (pTable->iDb==0 && pIndex->iDb==1) ); sqliteDeleteIndex(db, pIndex); } @@ -249,7 +267,9 @@ void sqliteDeleteTable(sqlite *db, Table *pTable){ */ for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){ pNextFKey = pFKey->pNextFrom; - assert( sqliteHashFind(&db->aFKey,pFKey->zTo,strlen(pFKey->zTo)+1)!=pFKey ); + assert( pTable->iDb<db->nDb ); + assert( sqliteHashFind(&db->aDb[pTable->iDb].aFKey, + pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey ); sqliteFree(pFKey); } @@ -273,14 +293,15 @@ void sqliteDeleteTable(sqlite *db, Table *pTable){ static void sqliteUnlinkAndDeleteTable(sqlite *db, Table *p){ Table *pOld; FKey *pF1, *pF2; + int i = p->iDb; assert( db!=0 ); - pOld = sqliteHashInsert(&db->tblHash, p->zName, strlen(p->zName)+1, 0); + pOld = sqliteHashInsert(&db->aDb[i].tblHash, p->zName, strlen(p->zName)+1, 0); assert( pOld==0 || pOld==p ); for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){ int nTo = strlen(pF1->zTo) + 1; - pF2 = sqliteHashFind(&db->aFKey, pF1->zTo, nTo); + pF2 = sqliteHashFind(&db->aDb[i].aFKey, pF1->zTo, nTo); if( pF2==pF1 ){ - sqliteHashInsert(&db->aFKey, pF1->zTo, nTo, pF1->pNextTo); + sqliteHashInsert(&db->aDb[i].aFKey, pF1->zTo, nTo, pF1->pNextTo); }else{ while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; } if( pF2 ){ @@ -347,7 +368,9 @@ void sqliteStartTable( pParse->sFirstToken = *pStart; zName = sqliteTableNameFromToken(pName); if( zName==0 ) return; + if( pParse->iDb==1 ) isTemp = 1; #ifndef SQLITE_OMIT_AUTHORIZATION + assert( (isTemp & 1)==isTemp ); if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0) ){ sqliteFree(zName); return; @@ -378,7 +401,7 @@ void sqliteStartTable( /* Before trying to create a temporary table, make sure the Btree for ** holding temporary tables is open. */ - if( isTemp && db->aDb[1].pBt==0 ){ + if( isTemp && db->aDb[1].pBt==0 && !pParse->explain ){ int rc = sqliteBtreeOpen(0, 0, MAX_PAGES, &db->aDb[1].pBt); if( rc!=SQLITE_OK ){ sqliteSetString(&pParse->zErrMsg, "unable to open a temporary database " @@ -402,26 +425,18 @@ void sqliteStartTable( ** ** 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. + ** an existing temporary table, that is not an error. */ - pTable = sqliteFindTable(db, zName); - if( pTable!=0 ){ - 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; + pTable = sqliteFindTable(db, zName, 0); + if( pTable!=0 && (pTable->iDb==isTemp || !pParse->initFlag) ){ + sqliteSetNString(&pParse->zErrMsg, "table ", 0, pName->z, pName->n, + " already exists", 0, 0); + sqliteFree(zName); + pParse->nErr++; + return; } - if( (pIdx = sqliteFindIndex(db, zName))!=0 && - (!pIdx->pTable->isTemp || !pParse->initFlag) ){ + if( (pIdx = sqliteFindIndex(db, zName, 0))!=0 && + (pIdx->iDb==0 || !pParse->initFlag) ){ sqliteSetString(&pParse->zErrMsg, "there is already an index named ", zName, 0); sqliteFree(zName); @@ -438,7 +453,7 @@ void sqliteStartTable( pTable->aCol = 0; pTable->iPKey = -1; pTable->pIndex = 0; - pTable->isTemp = isTemp; + pTable->iDb = isTemp ? 1 : pParse->iDb; if( pParse->pNewTable ) sqliteDeleteTable(db, pParse->pNewTable); pParse->pNewTable = pTable; @@ -624,7 +639,7 @@ void sqliteAddPrimaryKey(Parse *pParse, IdList *pList, int onError){ pTab->iPKey = iCol; pTab->keyConf = onError; }else{ - sqliteCreateIndex(pParse, 0, 0, pList, onError, 0, 0); + sqliteCreateIndex(pParse, 0, 0, pList, onError, 0, 0, 0); pList = 0; } @@ -778,7 +793,7 @@ static char *createTableStmt(Table *p){ n += 35 + 6*p->nCol; zStmt = sqliteMallocRaw( n ); if( zStmt==0 ) return 0; - strcpy(zStmt, p->isTemp ? "CREATE TEMP TABLE " : "CREATE TABLE "); + strcpy(zStmt, p->iDb==1 ? "CREATE TEMP TABLE " : "CREATE TABLE "); k = strlen(zStmt); identPut(zStmt, &k, p->zName); zStmt[k++] = '('; @@ -859,7 +874,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){ if( v==0 ) return; if( p->pSelect==0 ){ /* A regular table */ - sqliteVdbeAddOp(v, OP_CreateTable, 0, p->isTemp); + sqliteVdbeAddOp(v, OP_CreateTable, 0, p->iDb); sqliteVdbeChangeP3(v, -1, (char *)&p->tnum, P3_POINTER); }else{ /* A view */ @@ -891,12 +906,12 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){ } sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0); sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0); - if( !p->isTemp ){ + if( !p->iDb ){ sqliteChangeCookie(db, v); } sqliteVdbeAddOp(v, OP_Close, 0, 0); if( pSelect ){ - sqliteVdbeAddOp(v, OP_Integer, p->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, p->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0); pParse->nTab = 2; sqliteSelect(pParse, pSelect, SRT_Table, 1, 0, 0, 0); @@ -906,19 +921,19 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){ /* Add the table to the in-memory representation of the database. */ - assert( pParse->nameClash==0 || pParse->initFlag==1 ); - if( pParse->explain==0 && pParse->nameClash==0 && pParse->nErr==0 ){ + if( pParse->explain==0 && pParse->nErr==0 ){ Table *pOld; FKey *pFKey; - pOld = sqliteHashInsert(&db->tblHash, p->zName, strlen(p->zName)+1, p); + pOld = sqliteHashInsert(&db->aDb[p->iDb].tblHash, + p->zName, strlen(p->zName)+1, p); if( pOld ){ assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ return; } for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){ int nTo = strlen(pFKey->zTo) + 1; - pFKey->pNextTo = sqliteHashFind(&db->aFKey, pFKey->zTo, nTo); - sqliteHashInsert(&db->aFKey, pFKey->zTo, nTo, pFKey); + pFKey->pNextTo = sqliteHashFind(&db->aDb[p->iDb].aFKey, pFKey->zTo, nTo); + sqliteHashInsert(&db->aDb[p->iDb].aFKey, pFKey->zTo, nTo, pFKey); } pParse->pNewTable = 0; db->nTable++; @@ -1038,7 +1053,7 @@ int sqliteViewGetColumnNames(Parse *pParse, Table *pTable){ pSelTab->nCol = 0; pSelTab->aCol = 0; sqliteDeleteTable(0, pSelTab); - pParse->db->flags |= SQLITE_UnresetViews; + pParse->db->aDb[pTable->iDb].flags |= SQLITE_UnresetViews; }else{ pTable->nCol = 0; nErr++; @@ -1074,16 +1089,16 @@ static void sqliteViewResetColumnNames(Table *pTable){ /* ** Clear the column names from every VIEW. */ -void sqliteViewResetAll(sqlite *db){ +static void sqliteViewResetAll(sqlite *db, int idx){ HashElem *i; - if( (db->flags & SQLITE_UnresetViews)==0 ) return; - for(i=sqliteHashFirst(&db->tblHash); i; i=sqliteHashNext(i)){ + if( (db->aDb[idx].flags & SQLITE_UnresetViews)==0 ) return; + for(i=sqliteHashFirst(&db->aDb[idx].tblHash); i; i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); if( pTab->pSelect ){ sqliteViewResetColumnNames(pTab); } } - db->flags &= ~SQLITE_UnresetViews; + db->aDb[idx].flags &= ~SQLITE_UnresetViews; } /* @@ -1095,7 +1110,7 @@ Table *sqliteTableFromToken(Parse *pParse, Token *pTok){ Table *pTab; zName = sqliteTableNameFromToken(pTok); if( zName==0 ) return 0; - pTab = sqliteFindTable(pParse->db, zName); + pTab = sqliteFindTable(pParse->db, zName, 0); sqliteFree(zName); if( pTab==0 ){ sqliteSetNString(&pParse->zErrMsg, "no such table: ", 0, @@ -1114,24 +1129,26 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){ Vdbe *v; int base; sqlite *db = pParse->db; + int iDb; if( pParse->nErr || sqlite_malloc_failed ) return; pTable = sqliteTableFromToken(pParse, pName); if( pTable==0 ) return; + iDb = pTable->iDb; #ifndef SQLITE_OMIT_AUTHORIZATION - if( sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTable->isTemp),0)){ + if( sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTable->iDb),0)){ return; } { int code; if( isView ){ - if( pTable->isTemp ){ + if( iDb==1 ){ code = SQLITE_DROP_TEMP_VIEW; }else{ code = SQLITE_DROP_VIEW; } }else{ - if( pTable->isTemp ){ + if( iDb==1 ){ code = SQLITE_DROP_TEMP_TABLE; }else{ code = SQLITE_DROP_TABLE; @@ -1181,15 +1198,17 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){ }; Index *pIdx; Trigger *pTrigger; - sqliteBeginWriteOperation(pParse, 0, pTable->isTemp); - sqliteOpenMasterTable(v, pTable->isTemp); + sqliteBeginWriteOperation(pParse, 0, pTable->iDb); + sqliteOpenMasterTable(v, pTable->iDb); /* Drop all triggers associated with the table being dropped */ pTrigger = pTable->pTrigger; while( pTrigger ){ - Token tt; - tt.z = pTable->pTrigger->name; - tt.n = strlen(pTable->pTrigger->name); - sqliteDropTrigger(pParse, &tt, 1); + SrcList *pNm; + assert( pTrigger->iDb==pTable->iDb ); + pNm = sqliteSrcListAppend(0, 0, 0); + pNm->a[0].zName = sqliteStrDup(pTrigger->name); + pNm->a[0].zDatabase = sqliteStrDup(db->aDb[pTable->iDb].zName); + sqliteDropTrigger(pParse, pNm, 1); if( pParse->explain ){ pTrigger = pTrigger->pNext; }else{ @@ -1198,14 +1217,14 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){ } base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); sqliteVdbeChangeP3(v, base+1, pTable->zName, 0); - if( !pTable->isTemp ){ + if( !pTable->iDb ){ sqliteChangeCookie(db, v); } sqliteVdbeAddOp(v, OP_Close, 0, 0); if( !isView ){ - sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->isTemp); + sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->iDb); for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pTable->isTemp); + sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pTable->iDb); } } sqliteEndWriteOperation(pParse); @@ -1220,7 +1239,7 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){ sqliteUnlinkAndDeleteTable(db, pTable); db->flags |= SQLITE_InternChanges; } - sqliteViewResetAll(db); + sqliteViewResetAll(db, iDb); } /* @@ -1403,9 +1422,10 @@ void sqliteDeferForeignKey(Parse *pParse, int isDeferred){ void sqliteCreateIndex( Parse *pParse, /* All information about this parse */ Token *pName, /* Name of the index. May be NULL */ - Token *pTable, /* Name of the table to index. Use pParse->pNewTable if 0 */ + SrcList *pTable, /* Name of the table to index. Use pParse->pNewTable if 0 */ IdList *pList, /* A list of columns to be indexed */ int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + int isTemp, /* True if this is a temporary index */ Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */ Token *pEnd /* The ")" that closes the CREATE INDEX statement */ ){ @@ -1415,7 +1435,6 @@ void sqliteCreateIndex( int i, j; 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; @@ -1424,15 +1443,17 @@ void sqliteCreateIndex( */ if( pTable!=0 ){ assert( pName!=0 ); - pTab = sqliteTableFromToken(pParse, pTable); + assert( pTable->nSrc==1 ); + pTab = sqliteTableNameToTable(pParse, + pTable->a[0].zName, pTable->a[0].zDatabase); }else{ assert( pName==0 ); pTab = pParse->pNewTable; } if( pTab==0 || pParse->nErr ) goto exit_create_index; - if( pTab->readOnly ){ + if( !isTemp && (pTab->readOnly || pTab->iDb>=2) ){ sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName, - " may not have new indices added", 0); + " may not have non-temporary indices added", 0); pParse->nErr++; goto exit_create_index; } @@ -1441,7 +1462,12 @@ void sqliteCreateIndex( pParse->nErr++; goto exit_create_index; } + if( pTab->iDb==1 ){ + isTemp = 1; + } + +#if 0 /* 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 that this index is really associated with is @@ -1449,9 +1475,10 @@ void sqliteCreateIndex( ** Since its table has been suppressed, we need to also suppress the ** index. */ - if( pParse->initFlag && !pParse->isTemp && pTab->isTemp ){ + if( pParse->initFlag && !pParse->isTemp && pTab->iDb ){ goto exit_create_index; } +#endif /* ** Find the name of the index. Make sure there is not already another @@ -1460,40 +1487,30 @@ void sqliteCreateIndex( ** 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. + ** index, then we will continue to process this index. ** ** 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 ){ + if( pName && !pParse->initFlag ){ Index *pISameName; /* Another index with the same name */ Table *pTSameName; /* A table with same name as the index */ - zName = sqliteTableNameFromToken(pName); + zName = sqliteStrNDup(pName->z, pName->n); if( zName==0 ) 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( (pISameName = sqliteFindIndex(db, zName, 0))!=0 ){ + sqliteSetString(&pParse->zErrMsg, "index ", zName, + " already exists", 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; - } + if( (pTSameName = sqliteFindTable(db, zName, 0))!=0 ){ + sqliteSetString(&pParse->zErrMsg, "there is already a table named ", + zName, 0); + pParse->nErr++; + goto exit_create_index; } - }else{ + }else if( pName==0 ){ char zBuf[30]; int n; Index *pLoop; @@ -1502,17 +1519,20 @@ void sqliteCreateIndex( zName = 0; sqliteSetString(&zName, "(", pTab->zName, " autoindex ", zBuf, 0); if( zName==0 ) goto exit_create_index; - hideName = sqliteFindIndex(db, zName)!=0; + }else{ + zName = sqliteStrNDup(pName->z, pName->n); } /* Check for authorization to create an index. */ #ifndef SQLITE_OMIT_AUTHORIZATION - if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(pTab->isTemp), 0) ){ + assert( isTemp==0 || isTemp==1 ); + assert( pTab->iDb==pParse->iDb || isTemp==1 ); + if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0) ){ goto exit_create_index; } i = SQLITE_CREATE_INDEX; - if( pTab->isTemp ) i = SQLITE_CREATE_TEMP_INDEX; + if( isTemp ) i = SQLITE_CREATE_TEMP_INDEX; if( sqliteAuthCheck(pParse, i, zName, pTab->zName) ){ goto exit_create_index; } @@ -1542,6 +1562,7 @@ void sqliteCreateIndex( pIndex->nColumn = pList->nId; pIndex->onError = pIndex->isUnique = onError; pIndex->autoIndex = pName==0; + pIndex->iDb = isTemp ? 1 : pParse->iDb; /* Scan the names of the columns of the table to be indexed and ** load the column indices into the Index structure. Report an error @@ -1564,9 +1585,10 @@ void sqliteCreateIndex( /* Link the new Index structure to its table and to the other ** in-memory database structures. */ - if( !pParse->explain && !hideName ){ + if( !pParse->explain ){ Index *p; - p = sqliteHashInsert(&db->idxHash, pIndex->zName, strlen(zName)+1, pIndex); + p = sqliteHashInsert(&db->aDb[isTemp].idxHash, + pIndex->zName, strlen(zName)+1, pIndex); if( p ){ assert( p==pIndex ); /* Malloc must have failed */ sqliteFree(pIndex); @@ -1622,7 +1644,6 @@ void sqliteCreateIndex( int lbl1, lbl2; int i; int addr; - int isTemp = pTab->isTemp; v = sqliteGetVdbe(pParse); if( v==0 ) goto exit_create_index; @@ -1653,7 +1674,7 @@ void sqliteCreateIndex( sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0); sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0); if( pTable ){ - sqliteVdbeAddOp(v, OP_Integer, isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, 2, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); lbl2 = sqliteVdbeMakeLabel(v); @@ -1683,6 +1704,7 @@ void sqliteCreateIndex( /* Clean up before exiting */ exit_create_index: sqliteIdListDelete(pList); + sqliteSrcListDelete(pTable); sqliteFree(zName); return; } @@ -1691,39 +1713,41 @@ exit_create_index: ** This routine will drop an existing named index. This routine ** implements the DROP INDEX statement. */ -void sqliteDropIndex(Parse *pParse, Token *pName){ +void sqliteDropIndex(Parse *pParse, SrcList *pName){ Index *pIndex; - char *zName; Vdbe *v; sqlite *db = pParse->db; if( pParse->nErr || sqlite_malloc_failed ) return; - zName = sqliteTableNameFromToken(pName); - if( zName==0 ) return; - pIndex = sqliteFindIndex(db, zName); - sqliteFree(zName); + assert( pName->nSrc==1 ); + pIndex = sqliteFindIndex(db, pName->a[0].zName, pName->a[0].zDatabase); if( pIndex==0 ){ - sqliteSetNString(&pParse->zErrMsg, "no such index: ", 0, - pName->z, pName->n, 0); + sqliteSetString(&pParse->zErrMsg, "no such index: ", pName->a[0].zName, 0); pParse->nErr++; - return; + goto exit_drop_index; } if( pIndex->autoIndex ){ sqliteSetString(&pParse->zErrMsg, "index associated with UNIQUE " "or PRIMARY KEY constraint cannot be dropped", 0); pParse->nErr++; - return; + goto exit_drop_index; + } + if( pIndex->iDb>1 ){ + sqliteSetString(&pParse->zErrMsg, "cannot alter schema of attached " + "databases", 0); + pParse->nErr++; + goto exit_drop_index; } #ifndef SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_DROP_INDEX; Table *pTab = pIndex->pTable; - if( sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTab->isTemp), 0) ){ - return; + if( sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pIndex->iDb), 0) ){ + goto exit_drop_index; } - if( pTab->isTemp ) code = SQLITE_DROP_TEMP_INDEX; + if( pIndex->iDb ) code = SQLITE_DROP_TEMP_INDEX; if( sqliteAuthCheck(pParse, code, pIndex->zName, pTab->zName) ){ - return; + goto exit_drop_index; } } #endif @@ -1743,17 +1767,16 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ { OP_Delete, 0, 0, 0}, /* 8 */ }; int base; - Table *pTab = pIndex->pTable; - sqliteBeginWriteOperation(pParse, 0, pTab->isTemp); - sqliteOpenMasterTable(v, pTab->isTemp); + sqliteBeginWriteOperation(pParse, 0, pIndex->iDb); + sqliteOpenMasterTable(v, pIndex->iDb); base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); sqliteVdbeChangeP3(v, base+1, pIndex->zName, 0); - if( !pTab->isTemp ){ + if( pIndex->iDb==0 ){ sqliteChangeCookie(db, v); } sqliteVdbeAddOp(v, OP_Close, 0, 0); - sqliteVdbeAddOp(v, OP_Destroy, pIndex->tnum, pTab->isTemp); + sqliteVdbeAddOp(v, OP_Destroy, pIndex->tnum, pIndex->iDb); sqliteEndWriteOperation(pParse); } @@ -1763,6 +1786,9 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ sqliteUnlinkAndDeleteIndex(db, pIndex); db->flags |= SQLITE_InternChanges; } + +exit_drop_index: + sqliteSrcListDelete(pName); } /* @@ -1943,13 +1969,12 @@ void sqliteSrcListDelete(SrcList *pList){ */ void sqliteCopy( Parse *pParse, /* The parser context */ - Token *pTableName, /* The name of the table into which we will insert */ + SrcList *pTableName, /* The name of the table into which we will insert */ Token *pFilename, /* The file from which to obtain information */ Token *pDelimiter, /* Use this as the field delimiter */ int onError /* What to do if a constraint fails */ ){ Table *pTab; - char *zTab; int i; Vdbe *v; int addr, end; @@ -1958,10 +1983,10 @@ void sqliteCopy( sqlite *db = pParse->db; - zTab = sqliteTableNameFromToken(pTableName); - if( sqlite_malloc_failed || zTab==0 ) goto copy_cleanup; - pTab = sqliteTableNameToTable(pParse, zTab); - sqliteFree(zTab); + if( sqlite_malloc_failed ) goto copy_cleanup; + assert( pTableName->nSrc==1 ); + pTab = sqliteTableNameToTable(pParse, pTableName->a[0].zName, + pTableName->a[0].zDatabase); if( pTab==0 ) goto copy_cleanup; zFile = sqliteStrNDup(pFilename->z, pFilename->n); sqliteDequote(zFile); @@ -1971,15 +1996,16 @@ void sqliteCopy( } v = sqliteGetVdbe(pParse); if( v ){ - sqliteBeginWriteOperation(pParse, 1, pTab->isTemp); + sqliteBeginWriteOperation(pParse, 1, pTab->iDb==1); addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0); sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n); sqliteVdbeDequoteP3(v, addr); - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, 0, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + assert( pIdx->iDb==1 || pIdx->iDb==pTab->iDb ); + sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, i, pIdx->tnum); sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC); } @@ -2026,6 +2052,7 @@ void sqliteCopy( } copy_cleanup: + sqliteSrcListDelete(pTableName); sqliteFree(zFile); return; } @@ -2487,7 +2514,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ if( sqliteStrICmp(zLeft, "table_info")==0 ){ Table *pTab; - pTab = sqliteFindTable(db, zRight); + pTab = sqliteFindTable(db, zRight, 0); if( pTab ){ static VdbeOp tableInfoPreface[] = { { OP_ColumnName, 0, 0, "cid"}, @@ -2517,7 +2544,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ if( sqliteStrICmp(zLeft, "index_info")==0 ){ Index *pIdx; Table *pTab; - pIdx = sqliteFindIndex(db, zRight); + pIdx = sqliteFindIndex(db, zRight, 0); if( pIdx ){ static VdbeOp tableInfoPreface[] = { { OP_ColumnName, 0, 0, "seqno"}, @@ -2542,7 +2569,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ if( sqliteStrICmp(zLeft, "index_list")==0 ){ Index *pIdx; Table *pTab; - pTab = sqliteFindTable(db, zRight); + pTab = sqliteFindTable(db, zRight, 0); if( pTab ){ v = sqliteGetVdbe(pParse); pIdx = pTab->pIndex; diff --git a/src/delete.c b/src/delete.c index c55c6f37e..770a40082 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.47 2003/03/20 01:16:59 drh Exp $ +** $Id: delete.c,v 1.48 2003/03/27 12:51:24 drh Exp $ */ #include "sqliteInt.h" @@ -22,11 +22,15 @@ ** table is writeable. Generate an error and return NULL if not. If ** everything checks out, return a pointer to the Table structure. */ -Table *sqliteTableNameToTable(Parse *pParse, const char *zTab){ +Table *sqliteTableNameToTable(Parse *pParse, const char *zTab, const char *zDb){ Table *pTab; - pTab = sqliteFindTable(pParse->db, zTab); + pTab = sqliteFindTable(pParse->db, zTab, zDb); if( pTab==0 ){ - sqliteSetString(&pParse->zErrMsg, "no such table: ", zTab, 0); + if( zDb==0 || zDb[0]==0 ){ + sqliteSetString(&pParse->zErrMsg, "no such table: ", zTab, 0); + }else{ + sqliteSetString(&pParse->zErrMsg, "no such table: ", zDb, ".", zTab, 0); + } pParse->nErr++; return 0; } @@ -52,6 +56,7 @@ void sqliteDeleteFrom( Vdbe *v; /* The virtual database engine */ Table *pTab; /* The table from which records will be deleted */ char *zTab; /* Name of the table from which we are deleting */ + char *zDb; /* Name of database containing table zTab */ int end, addr; /* A couple addresses of generated code */ int i; /* Loop counter */ WhereInfo *pWInfo; /* Information about the WHERE clause */ @@ -73,8 +78,9 @@ void sqliteDeleteFrom( ** defined */ zTab = pTabList->a[0].zName; + zDb = pTabList->a[0].zDatabase; if( zTab != 0 ){ - pTab = sqliteFindTable(pParse->db, zTab); + pTab = sqliteFindTable(pParse->db, zTab, zDb); if( pTab ){ row_triggers_exist = sqliteTriggersExist(pParse, pTab->pTrigger, @@ -95,7 +101,7 @@ void sqliteDeleteFrom( ** will be calling are designed to work with multiple tables and expect ** an SrcList* parameter instead of just a Table* parameter. */ - pTab = pTabList->a[0].pTab = sqliteTableNameToTable(pParse, zTab); + pTab = pTabList->a[0].pTab = sqliteTableNameToTable(pParse, zTab, zDb); if( pTab==0 ){ goto delete_from_cleanup; } @@ -129,7 +135,7 @@ void sqliteDeleteFrom( goto delete_from_cleanup; } sqliteBeginWriteOperation(pParse, row_triggers_exist, - !row_triggers_exist && pTab->isTemp); + !row_triggers_exist && pTab->iDb==1); /* Initialize the counter of the number of rows deleted, if ** we are counting rows. @@ -148,7 +154,7 @@ void sqliteDeleteFrom( ** entries in the table. */ int endOfLoop = sqliteVdbeMakeLabel(v); int addr; - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum); sqliteVdbeAddOp(v, OP_Rewind, base, sqliteVdbeCurrentAddr(v)+2); addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0); @@ -156,9 +162,9 @@ void sqliteDeleteFrom( sqliteVdbeResolveLabel(v, endOfLoop); sqliteVdbeAddOp(v, OP_Close, base, 0); } - sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->isTemp); + sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pTab->isTemp); + sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb); } } @@ -195,7 +201,7 @@ void sqliteDeleteFrom( if( row_triggers_exist ){ addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); sqliteVdbeAddOp(v, OP_Dup, 0, 0); - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0); @@ -225,10 +231,10 @@ void sqliteDeleteFrom( ** cursors are opened only once on the outside the loop. */ pParse->nTab = base + 1; - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum); for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, pParse->nTab++, pIdx->tnum); } diff --git a/src/expr.c b/src/expr.c index 47a7dd1a8..576ae2db8 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,7 +12,7 @@ ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** -** $Id: expr.c,v 1.89 2003/03/20 01:16:59 drh Exp $ +** $Id: expr.c,v 1.90 2003/03/27 12:51:25 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -493,16 +493,29 @@ int sqliteExprResolveIds( break; } - /* A table name and column name: ID.ID */ + /* A table name and column name: ID.ID + ** Or a database, table and column: ID.ID.ID + */ case TK_DOT: { int cnt = 0; /* Number of matches */ int cntTab = 0; /* Number of matching tables */ int i; /* Loop counter */ Expr *pLeft, *pRight; /* Left and right subbranches of the expr */ char *zLeft, *zRight; /* Text of an identifier */ + char *zDb; /* Name of database holding table */ + sqlite *db = pParse->db; - pLeft = pExpr->pLeft; pRight = pExpr->pRight; + if( pRight->op==TK_ID ){ + pLeft = pExpr->pLeft; + zDb = 0; + }else{ + Expr *pDb = pExpr->pLeft; + assert( pDb && pDb->op==TK_ID && pDb->token.z ); + zDb = sqliteStrNDup(pDb->token.z, pDb->token.n); + pLeft = pRight->pLeft; + pRight = pRight->pRight; + } assert( pLeft && pLeft->op==TK_ID && pLeft->token.z ); assert( pRight && pRight->op==TK_ID && pRight->token.z ); zLeft = sqliteStrNDup(pLeft->token.z, pLeft->token.n); @@ -510,8 +523,10 @@ int sqliteExprResolveIds( if( zLeft==0 || zRight==0 ){ sqliteFree(zLeft); sqliteFree(zRight); + sqliteFree(zDb); return 1; } + sqliteDequote(zDb); sqliteDequote(zLeft); sqliteDequote(zRight); pExpr->iTable = -1; @@ -523,10 +538,14 @@ int sqliteExprResolveIds( assert( pTab->nCol>0 ); if( pTabList->a[i].zAlias ){ zTab = pTabList->a[i].zAlias; + if( sqliteStrICmp(zTab, zLeft)!=0 ) continue; }else{ zTab = pTab->zName; + if( zTab==0 || sqliteStrICmp(zTab, zLeft)!=0 ) continue; + if( zDb!=0 && sqliteStrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){ + continue; + } } - if( zTab==0 || sqliteStrICmp(zTab, zLeft)!=0 ) continue; if( 0==(cntTab++) ) pExpr->iTable = i + base; for(j=0; j<pTab->nCol; j++){ if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){ @@ -577,6 +596,7 @@ int sqliteExprResolveIds( pExpr->iColumn = -1; pExpr->dataType = SQLITE_SO_NUM; } + sqliteFree(zDb); sqliteFree(zLeft); sqliteFree(zRight); if( cnt==0 ){ @@ -592,9 +612,9 @@ int sqliteExprResolveIds( pParse->nErr++; return 1; } - sqliteExprDelete(pLeft); + sqliteExprDelete(pExpr->pLeft); pExpr->pLeft = 0; - sqliteExprDelete(pRight); + sqliteExprDelete(pExpr->pRight); pExpr->pRight = 0; pExpr->op = TK_COLUMN; sqliteAuthRead(pParse, pExpr, pTabList, base); diff --git a/src/func.c b/src/func.c index 5892b1888..98de567b2 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.23 2002/11/04 19:32:25 drh Exp $ +** $Id: func.c,v 1.24 2003/03/27 12:51:25 drh Exp $ */ #include <ctype.h> #include <math.h> @@ -255,6 +255,46 @@ static void versionFunc(sqlite_func *context, int argc, const char **argv){ sqlite_set_result_string(context, sqlite_version, -1); } +#ifdef SQLITE_SOUNDEX +/* +** Compute the soundex encoding of a word. +*/ +static void soundexFunc(sqlite_func *context, int argc, const char **argv){ + char zResult[8]; + const char *zIn; + int i, j; + static const unsigned char iCode[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, + 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, + 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, + }; + assert( argc==1 ); + zIn = argv[0]; + for(i=0; zIn[i] && !isalpha(zIn[i]); i++){} + if( zIn[i] ){ + zResult[0] = toupper(zIn[i]); + for(j=1; j<4 && zIn[i]; i++){ + int code = iCode[zIn[i]&0x7f]; + if( code>0 ){ + zResult[j++] = code + '0'; + } + } + while( j<4 ){ + zResult[j++] = '0'; + } + zResult[j] = 0; + sqlite_set_result_string(context, zResult, 4); + }else{ + sqlite_set_result_string(context, zResult, "?000", 4); + } +} +#endif + #ifdef SQLITE_TEST /* ** This function generates a string of random characters. Used for @@ -490,6 +530,9 @@ void sqliteRegisterBuiltinFunctions(sqlite *db){ { "glob", 2, SQLITE_NUMERIC, globFunc }, { "nullif", 2, SQLITE_ARGS, nullifFunc }, { "sqlite_version",0,SQLITE_TEXT, versionFunc}, +#ifdef SQLITE_SOUNDEX + { "soundex", 1, SQLITE_TEXT, soundexFunc}, +#endif #ifdef SQLITE_TEST { "randstr", 2, SQLITE_TEXT, randStr }, #endif diff --git a/src/insert.c b/src/insert.c index 5ed66168c..14f7824b0 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.74 2003/03/20 01:16:59 drh Exp $ +** $Id: insert.c,v 1.75 2003/03/27 12:51:25 drh Exp $ */ #include "sqliteInt.h" @@ -93,6 +93,7 @@ void sqliteInsert( ){ Table *pTab; /* The table to insert into */ char *zTab; /* Name of the table into which we are inserting */ + char *zDb; /* Name of the database holding zTab */ int i, j, idx; /* Loop counters */ Vdbe *v; /* Generate code into this virtual machine */ Index *pIdx; /* For looping over indices of the table */ @@ -120,10 +121,9 @@ void sqliteInsert( assert( pTabList->nSrc==1 ); zTab = pTabList->a[0].zName; if( zTab==0 ) goto insert_cleanup; - pTab = sqliteFindTable(pParse->db, zTab); + zDb = pTabList->a[0].zDatabase; + pTab = sqliteTableNameToTable(pParse, zTab, zDb); if( pTab==0 ){ - sqliteSetString(&pParse->zErrMsg, "no such table: ", zTab, 0); - pParse->nErr++; goto insert_cleanup; } if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0) ){ @@ -162,7 +162,7 @@ void sqliteInsert( v = sqliteGetVdbe(pParse); if( v==0 ) goto insert_cleanup; sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist, - !row_triggers_exist && pTab->isTemp); + !row_triggers_exist && pTab->iDb==1); /* if there are row triggers, allocate a temp table for new.* references. */ if( row_triggers_exist ){ @@ -196,7 +196,7 @@ void sqliteInsert( ** should be written into a temporary table. Set to FALSE if each ** row of the SELECT can be written directly into the result table. */ - opCode = pTab->isTemp ? OP_OpenTemp : OP_OpenRead; + opCode = pTab->iDb==1 ? OP_OpenTemp : OP_OpenRead; useTempTable = row_triggers_exist || sqliteVdbeFindOp(v,opCode,pTab->tnum); if( useTempTable ){ @@ -327,11 +327,11 @@ void sqliteInsert( /* Open tables and indices if there are no row triggers */ if( !row_triggers_exist ){ base = pParse->nTab; - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum); sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC); } @@ -389,11 +389,11 @@ void sqliteInsert( /* Open the tables and indices for the INSERT */ if( !pTab->pSelect ){ base = pParse->nTab; - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum); sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC); } diff --git a/src/main.c b/src/main.c index 60cf794dc..161bb454f 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.116 2003/03/20 01:16:59 drh Exp $ +** $Id: main.c,v 1.117 2003/03/27 12:51:25 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -67,7 +67,7 @@ int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){ memset(&sParse, 0, sizeof(sParse)); sParse.db = pData->db; sParse.initFlag = 1; - sParse.isTemp = argv[4][0] - '0'; + sParse.iDb = atoi(argv[4]); sParse.newTnum = atoi(argv[2]); sParse.useCallback = 1; sqliteRunParser(&sParse, argv[3], pData->pzErrMsg); @@ -78,7 +78,12 @@ int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){ ** been created when we processed the CREATE TABLE. All we have ** to do here is record the root page number for that index. */ - Index *pIndex = sqliteFindIndex(pData->db, argv[1]); + int iDb; + Index *pIndex; + + iDb = atoi(argv[4]); + assert( iDb>=0 && iDb<pData->db->nDb ); + pIndex = sqliteFindIndex(pData->db, argv[1], pData->db->aDb[iDb].zName); if( pIndex==0 || pIndex->tnum!=0 ){ /* This can occur if there exists an index on a TEMP table which ** has the same name as another index on a permanent index. Since @@ -118,7 +123,7 @@ int upgrade_3_callback(void *pInit, int argc, char **argv, char **NotUsed){ Trigger *pTrig; char *zErr = 0; - pTab = sqliteFindTable(pData->db, argv[0]); + pTab = sqliteFindTable(pData->db, argv[0], 0); assert( pTab!=0 ); assert( sqliteStrICmp(pTab->zName, argv[0])==0 ); if( pTab ){ @@ -141,7 +146,7 @@ int upgrade_3_callback(void *pInit, int argc, char **argv, char **NotUsed){ ** cause the structure that pTab points to be deleted. In case that ** happened, we need to refetch pTab. */ - pTab = sqliteFindTable(pData->db, argv[0]); + pTab = sqliteFindTable(pData->db, argv[0], 0); if( pTab ){ assert( sqliteStrICmp(pTab->zName, argv[0])==0 ); pTab->pTrigger = pTrig; /* Re-enable triggers */ @@ -234,7 +239,7 @@ int sqliteInit(sqlite *db, char **pzErrMsg){ initData.db = db; initData.pzErrMsg = pzErrMsg; sqliteInitCallback(&initData, 5, azArg, 0); - pTab = sqliteFindTable(db, MASTER_NAME); + pTab = sqliteFindTable(db, MASTER_NAME, "main"); if( pTab ){ pTab->readOnly = 1; } @@ -242,7 +247,7 @@ int sqliteInit(sqlite *db, char **pzErrMsg){ azArg[3] = temp_master_schema; azArg[4] = "1"; sqliteInitCallback(&initData, 5, azArg, 0); - pTab = sqliteFindTable(db, TEMP_MASTER_NAME); + pTab = sqliteFindTable(db, TEMP_MASTER_NAME, "temp"); if( pTab ){ pTab->readOnly = 1; } @@ -347,22 +352,24 @@ const char sqlite_encoding[] = "iso8859"; */ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){ sqlite *db; - int rc; + int rc, i; /* Allocate the sqlite data structure */ db = sqliteMalloc( sizeof(sqlite) ); if( pzErrMsg ) *pzErrMsg = 0; if( db==0 ) goto no_mem_on_open; - sqliteHashInit(&db->tblHash, SQLITE_HASH_STRING, 0); - sqliteHashInit(&db->idxHash, SQLITE_HASH_STRING, 0); - sqliteHashInit(&db->trigHash, SQLITE_HASH_STRING, 0); - sqliteHashInit(&db->aFunc, SQLITE_HASH_STRING, 1); - sqliteHashInit(&db->aFKey, SQLITE_HASH_STRING, 1); db->onError = OE_Default; db->priorNewRowid = 0; db->magic = SQLITE_MAGIC_BUSY; db->nDb = 2; db->aDb = db->aDbStatic; + sqliteHashInit(&db->aFunc, SQLITE_HASH_STRING, 1); + for(i=0; i<db->nDb; i++){ + sqliteHashInit(&db->aDb[i].tblHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&db->aDb[i].idxHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&db->aDb[i].trigHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&db->aDb[i].aFKey, SQLITE_HASH_STRING, 1); + } /* Open the backend database driver */ rc = sqliteBtreeOpen(zFilename, 0, MAX_PAGES, &db->aDb[0].pBt); @@ -486,7 +493,6 @@ void sqlite_close(sqlite *db){ } } sqliteHashClear(&db->aFunc); - sqliteHashClear(&db->aFKey); sqliteFree(db); } diff --git a/src/parse.y b/src/parse.y index 17d137d5f..e1feb992f 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.92 2003/03/20 01:16:59 drh Exp $ +** @(#) $Id: parse.y,v 1.93 2003/03/27 12:51:25 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -96,8 +96,8 @@ create_table ::= CREATE(X) temp(T) TABLE nm(Y). { sqliteStartTable(pParse,&X,&Y,T,0); } %type temp {int} -temp(A) ::= TEMP. {A = pParse->isTemp || !pParse->initFlag;} -temp(A) ::= . {A = pParse->isTemp;} +temp(A) ::= TEMP. {A = 1;} +temp(A) ::= . {A = 0;} create_table_args ::= LP columnlist conslist_opt RP(X). { sqliteEndTable(pParse,&X,0); } @@ -176,7 +176,7 @@ carg ::= DEFAULT NULL. ccons ::= NULL onconf. ccons ::= NOT NULL onconf(R). {sqliteAddNotNull(pParse, R);} ccons ::= PRIMARY KEY sortorder onconf(R). {sqliteAddPrimaryKey(pParse,0,R);} -ccons ::= UNIQUE onconf(R). {sqliteCreateIndex(pParse,0,0,0,R,0,0);} +ccons ::= UNIQUE onconf(R). {sqliteCreateIndex(pParse,0,0,0,R,0,0,0);} ccons ::= CHECK LP expr RP onconf. ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R). {sqliteCreateForeignKey(pParse,0,&T,TA,R);} @@ -223,7 +223,7 @@ tcons ::= CONSTRAINT nm. tcons ::= PRIMARY KEY LP idxlist(X) RP onconf(R). {sqliteAddPrimaryKey(pParse,X,R);} tcons ::= UNIQUE LP idxlist(X) RP onconf(R). - {sqliteCreateIndex(pParse,0,0,X,R,0,0);} + {sqliteCreateIndex(pParse,0,0,X,R,0,0,0);} tcons ::= CHECK expr onconf. tcons ::= FOREIGN KEY LP idxlist(FA) RP REFERENCES nm(T) idxlist_opt(TA) refargs(R) defer_subclause_opt(D). { @@ -528,6 +528,13 @@ expr(A) ::= nm(X) DOT nm(Y). { Expr *temp2 = sqliteExpr(TK_ID, 0, 0, &Y); A = sqliteExpr(TK_DOT, temp1, temp2, 0); } +expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { + Expr *temp1 = sqliteExpr(TK_ID, 0, 0, &X); + Expr *temp2 = sqliteExpr(TK_ID, 0, 0, &Y); + Expr *temp3 = sqliteExpr(TK_ID, 0, 0, &Z); + Expr *temp4 = sqliteExpr(TK_DOT, temp2, temp3, 0); + A = sqliteExpr(TK_DOT, temp1, temp4, 0); +} expr(A) ::= expr(B) ORACLE_OUTER_JOIN. {A = B; ExprSetProperty(A,EP_Oracle8Join);} expr(A) ::= INTEGER(X). {A = sqliteExpr(TK_INTEGER, 0, 0, &X);} @@ -692,11 +699,12 @@ expritem(A) ::= . {A = 0;} ///////////////////////////// The CREATE INDEX command /////////////////////// // -cmd ::= CREATE(S) uniqueflag(U) INDEX nm(X) - ON nm(Y) LP idxlist(Z) RP(E) onconf(R). { +cmd ::= CREATE(S) temp(T) uniqueflag(U) INDEX nm(X) + ON nm(Y) dbnm(D) LP idxlist(Z) RP(E) onconf(R). { + SrcList *pSrc = sqliteSrcListAppend(0, &Y, &D); if( U!=OE_None ) U = R; if( U==OE_Default) U = OE_Abort; - sqliteCreateIndex(pParse, &X, &Y, Z, U, &S, &E); + sqliteCreateIndex(pParse, &X, pSrc, Z, U, T, &S, &E); } %type uniqueflag {int} @@ -718,15 +726,17 @@ idxitem(A) ::= nm(X). {A = X;} ///////////////////////////// The DROP INDEX command ///////////////////////// // -cmd ::= DROP INDEX nm(X). {sqliteDropIndex(pParse, &X);} +cmd ::= DROP INDEX nm(X) dbnm(Y). { + sqliteDropIndex(pParse, sqliteSrcListAppend(0,&X,&Y)); +} ///////////////////////////// The COPY command /////////////////////////////// // -cmd ::= COPY orconf(R) nm(X) FROM nm(Y) USING DELIMITERS STRING(Z). - {sqliteCopy(pParse,&X,&Y,&Z,R);} -cmd ::= COPY orconf(R) nm(X) FROM nm(Y). - {sqliteCopy(pParse,&X,&Y,0,R);} +cmd ::= COPY orconf(R) nm(X) dbnm(D) FROM nm(Y) USING DELIMITERS STRING(Z). + {sqliteCopy(pParse,sqliteSrcListAppend(0,&X,&D),&Y,&Z,R);} +cmd ::= COPY orconf(R) nm(X) dbnm(D) FROM nm(Y). + {sqliteCopy(pParse,sqliteSrcListAppend(0,&X,&D),&Y,0,R);} ///////////////////////////// The VACUUM command ///////////////////////////// // @@ -749,13 +759,15 @@ plus_opt ::= PLUS. plus_opt ::= . //////////////////////////// The CREATE TRIGGER command ///////////////////// -cmd ::= CREATE(A) TRIGGER nm(B) trigger_time(C) trigger_event(D) ON nm(E) +cmd ::= CREATE(A) TRIGGER nm(B) trigger_time(C) trigger_event(D) + ON nm(E) dbnm(DB) foreach_clause(F) when_clause(G) BEGIN trigger_cmd_list(S) END(Z). { + SrcList *pTab = sqliteSrcListAppend(0, &E, &DB); Token all; all.z = A.z; all.n = (Z.z - A.z) + Z.n; - sqliteCreateTrigger(pParse, &B, C, D.a, D.b, &E, F, G, S, &all); + sqliteCreateTrigger(pParse, &B, C, D.a, D.b, pTab, F, G, S, &all); } %type trigger_time {int} @@ -828,8 +840,8 @@ expr(A) ::= RAISE(X) LP FAIL COMMA nm(Z) RP(Y). { } //////////////////////// DROP TRIGGER statement ////////////////////////////// -cmd ::= DROP TRIGGER nm(X). { - sqliteDropTrigger(pParse,&X,0); +cmd ::= DROP TRIGGER nm(X) dbnm(D). { + sqliteDropTrigger(pParse,sqliteSrcListAppend(0,&X,&D),0); } //////////////////////// ATTACH DATABASE file AS name ///////////////////////// diff --git a/src/select.c b/src/select.c index eb2892f24..48283550f 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.127 2003/03/19 03:14:02 drh Exp $ +** $Id: select.c,v 1.128 2003/03/27 12:51:25 drh Exp $ */ #include "sqliteInt.h" @@ -912,7 +912,8 @@ static int fillInColumnList(Parse *pParse, Select *p){ }else{ /* An ordinary table or view name in the FROM clause */ pTabList->a[i].pTab = pTab = - sqliteFindTable(pParse->db, pTabList->a[i].zName); + sqliteFindTable(pParse->db, pTabList->a[i].zName, + pTabList->a[i].zDatabase); if( pTab==0 ){ sqliteSetString(&pParse->zErrMsg, "no such table: ", pTabList->a[i].zName, 0); @@ -1830,13 +1831,13 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ sqliteCodeVerifySchema(pParse); } base = p->base; - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); if( pIdx==0 ){ sqliteVdbeAddOp(v, seekOp, base, 0); }else{ - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, base+1, pIdx->tnum); sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC); sqliteVdbeAddOp(v, seekOp, base+1, 0); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 0e7090ea4..854e255ea 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.164 2003/03/20 01:16:59 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.165 2003/03/27 12:51:25 drh Exp $ */ #include "config.h" #include "sqlite.h" @@ -200,7 +200,12 @@ struct Db { char *zName; /* Name of this database */ Btree *pBt; /* The B*Tree structure for this database file */ int schema_cookie; /* Database schema version number for this file */ + Hash tblHash; /* All tables indexed by name */ + Hash idxHash; /* All (named) indices indexed by name */ + Hash trigHash; /* All triggers indexed by name */ + Hash aFKey; /* Foreign keys indexed by to-table */ u8 inTrans; /* True if a transaction is underway for this backend */ + u16 flags; /* Flags associated with this database */ }; /* @@ -231,11 +236,7 @@ struct sqlite { int nTable; /* Number of tables in the database */ void *pBusyArg; /* 1st Argument to the busy callback */ int (*xBusyCallback)(void *,const char*,int); /* The busy callback */ - Hash tblHash; /* All tables indexed by name */ - Hash idxHash; /* All (named) indices indexed by name */ - Hash trigHash; /* All triggers indexed by name */ Hash aFunc; /* All functions that can be in SQL exprs */ - Hash aFKey; /* Foreign keys indexed by to-table */ int lastRowid; /* ROWID of most recent insert */ int priorNewRowid; /* Last randomly generated ROWID */ int onError; /* Default conflict algorithm */ @@ -329,26 +330,27 @@ struct Column { ** Each SQL table is represented in memory by an instance of the ** following structure. ** -** Expr.zName is the name of the table. The case of the original +** Table.zName is the name of the table. The case of the original ** CREATE TABLE statement is stored, but case is not significant for ** comparisons. ** -** Expr.nCol is the number of columns in this table. Expr.aCol is a +** Table.nCol is the number of columns in this table. Table.aCol is a ** pointer to an array of Column structures, one for each column. ** -** If the table has an INTEGER PRIMARY KEY, then Expr.iPKey is the index of -** the column that is that key. Otherwise Expr.iPKey is negative. Note +** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of +** the column that is that key. Otherwise Table.iPKey is negative. Note ** that the datatype of the PRIMARY KEY must be INTEGER for this field to ** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of ** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid -** is generated for each row of the table. Expr.hasPrimKey is true if +** is generated for each row of the table. Table.hasPrimKey is true if ** the table has any PRIMARY KEY, INTEGER or otherwise. ** -** Expr.tnum is the page number for the root BTree page of the table in the -** database file. If Expr.isTemp is true, then this page occurs in the -** auxiliary database file, not the main database file. If Expr.isTransient +** Table.tnum is the page number for the root BTree page of the table in the +** database file. If Table.iDb is the index of the database table backend +** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that +** holds temporary tables and indices. If Table.isTransient ** is true, then the table is stored in a file that is automatically deleted -** when the VDBE cursor to the table is closed. In this case Expr.tnum +** when the VDBE cursor to the table is closed. In this case Table.tnum ** refers VDBE cursor number that holds the table open, not to the root ** page number. Transient tables are used to hold the results of a ** sub-query that appears instead of a real table name in the FROM clause @@ -363,7 +365,7 @@ struct Table { int tnum; /* Root BTree node for this table (see note above) */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ u8 readOnly; /* True if this table should not be written by the user */ - u8 isTemp; /* Index into sqlite.aDb[] of the backend for this table */ + u8 iDb; /* Index into sqlite.aDb[] of the backend for this table */ u8 isTransient; /* True if automatically deleted when VDBE finishes */ u8 hasPrimKey; /* True if there exists a primary key */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ @@ -479,6 +481,7 @@ struct Index { u8 isUnique; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ + u8 iDb; /* Index in sqlite.aDb[] of where this index is stored */ Index *pNext; /* The next index associated with the same table */ }; @@ -604,6 +607,12 @@ struct IdList { ** The following structure describes the FROM clause of a SELECT statement. ** Each table or subquery in the FROM clause is a separate element of ** the SrcList.a[] array. +** +** With the addition of multiple database support, the following structure +** can also be used to describe a particular table such as the table that +** is modified by an INSERT, DELETE, or UPDATE statement. In standard SQL, +** such a table must be a simple name: ID. But in SQLite, the table can +** now be identified by a database name, a dot, then the table name: ID.ID. */ struct SrcList { int nSrc; /* Number of tables or subqueries in the FROM clause */ @@ -765,7 +774,7 @@ struct Parse { ** while generating expressions. Normally false */ u8 schemaVerified; /* True if an OP_VerifySchema has been coded someplace ** other than after an OP_Transaction */ - u8 isTemp; /* True if parsing temporary tables */ + u8 iDb; /* Index of database whose schema is being parsed */ u8 useCallback; /* True if callbacks should be used to report results */ int newTnum; /* Table number to use when reparsing CREATE TABLEs */ int nErr; /* Number of errors seen */ @@ -807,6 +816,7 @@ struct Parse { struct Trigger { char *name; /* The name of the trigger */ char *table; /* The table or view to which the trigger applies */ + int iDb; /* Database containing this trigger */ int op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ int tr_tm; /* One of TK_BEFORE, TK_AFTER */ Expr *pWhen; /* The WHEN clause of the expresion (may be NULL) */ @@ -973,7 +983,6 @@ void sqliteAddCollateType(Parse*, int); void sqliteEndTable(Parse*,Token*,Select*); void sqliteCreateView(Parse*,Token*,Token*,Select*,int); int sqliteViewGetColumnNames(Parse*,Table*); -void sqliteViewResetAll(sqlite*); void sqliteDropTable(Parse*, Token*, int); void sqliteDeleteTable(sqlite*, Table*); void sqliteInsert(Parse*, SrcList*, ExprList*, Select*, IdList*, int); @@ -983,8 +992,8 @@ SrcList *sqliteSrcListAppend(SrcList*, Token*, Token*); void sqliteSrcListAddAlias(SrcList*, Token*); void sqliteIdListDelete(IdList*); void sqliteSrcListDelete(SrcList*); -void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, int, Token*, Token*); -void sqliteDropIndex(Parse*, Token*); +void sqliteCreateIndex(Parse*,Token*,SrcList*,IdList*,int,int,Token*,Token*); +void sqliteDropIndex(Parse*, SrcList*); void sqliteAddKeyType(Vdbe*, ExprList*); void sqliteAddIdxKeyType(Vdbe*, Index*); int sqliteSelect(Parse*, Select*, int, int, Select*, int, int*); @@ -992,7 +1001,7 @@ Select *sqliteSelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*, int,int,int); void sqliteSelectDelete(Select*); void sqliteSelectUnbind(Select*); -Table *sqliteTableNameToTable(Parse*, const char*); +Table *sqliteTableNameToTable(Parse*, const char*, const char*); void sqliteDeleteFrom(Parse*, SrcList*, Expr*); void sqliteUpdate(Parse*, SrcList*, ExprList*, Expr*, int); WhereInfo *sqliteWhereBegin(Parse*, int, SrcList*, Expr*, int, ExprList**); @@ -1000,10 +1009,10 @@ void sqliteWhereEnd(WhereInfo*); void sqliteExprCode(Parse*, Expr*); void sqliteExprIfTrue(Parse*, Expr*, int, int); void sqliteExprIfFalse(Parse*, Expr*, int, int); -Table *sqliteFindTable(sqlite*,const char*); -Index *sqliteFindIndex(sqlite*,const char*); +Table *sqliteFindTable(sqlite*,const char*, const char*); +Index *sqliteFindIndex(sqlite*,const char*, const char*); void sqliteUnlinkAndDeleteIndex(sqlite*,Index*); -void sqliteCopy(Parse*, Token*, Token*, Token*, int); +void sqliteCopy(Parse*, SrcList*, Token*, Token*, int); void sqliteVacuum(Parse*, Token*); int sqliteGlobCompare(const unsigned char*,const unsigned char*); int sqliteLikeCompare(const unsigned char*,const unsigned char*); @@ -1043,9 +1052,9 @@ int sqliteSafetyOn(sqlite*); int sqliteSafetyOff(sqlite*); int sqliteSafetyCheck(sqlite*); void sqliteChangeCookie(sqlite*, Vdbe*); -void sqliteCreateTrigger(Parse*, Token*, int, int, IdList*, Token*, +void sqliteCreateTrigger(Parse*, Token*, int, int, IdList*, SrcList*, int, Expr*, TriggerStep*, Token*); -void sqliteDropTrigger(Parse*, Token*, int); +void sqliteDropTrigger(Parse*, SrcList*, int); int sqliteTriggersExist(Parse* , Trigger* , int , int , int, ExprList*); int sqliteCodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, int, int); diff --git a/src/trigger.c b/src/trigger.c index 76bdc77ba..4c0c00740 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -41,7 +41,7 @@ void sqliteCreateTrigger( int tr_tm, /* One of TK_BEFORE, TK_AFTER , TK_INSTEAD */ int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */ IdList *pColumns, /* column list if this is an UPDATE OF trigger */ - Token *pTableName, /* The name of the table/view the trigger applies to */ + SrcList *pTableName,/* The name of the table/view the trigger applies to */ int foreach, /* One of TK_ROW or TK_STATEMENT */ Expr *pWhen, /* WHEN clause */ TriggerStep *pStepList, /* The triggered program */ @@ -50,6 +50,7 @@ void sqliteCreateTrigger( Trigger *nt; Table *tab; char *zName = 0; /* Name of the trigger */ + sqlite *db = pParse->db; /* Check that: ** 1. the trigger name does not already exist. @@ -58,60 +59,55 @@ void sqliteCreateTrigger( ** 4. That we are not trying to create an INSTEAD OF trigger on a table. ** 5. That we are not trying to create a BEFORE or AFTER trigger on a view. */ + if( sqlite_malloc_failed ) goto trigger_cleanup; + assert( pTableName->nSrc==1 ); + tab = sqliteTableNameToTable(pParse, pTableName->a[0].zName, + pTableName->a[0].zDatabase); + if( !tab ){ + goto trigger_cleanup; + } + if( tab->iDb>=2 ){ + sqliteSetString(&pParse->zErrMsg, "triggers may not be added to " + "auxiliary database \"", db->aDb[tab->iDb].zName, "\"", 0); + pParse->nErr++; + goto trigger_cleanup; + } + zName = sqliteStrNDup(pName->z, pName->n); - if( sqliteHashFind(&(pParse->db->trigHash), zName, pName->n + 1) ){ + if( sqliteHashFind(&(db->aDb[tab->iDb].trigHash), zName,pName->n+1) ){ sqliteSetNString(&pParse->zErrMsg, "trigger ", -1, pName->z, pName->n, " already exists", -1, 0); pParse->nErr++; goto trigger_cleanup; } + if( sqliteStrNICmp(tab->zName, "sqlite_", 7)==0 ){ + sqliteSetString(&pParse->zErrMsg,"cannot create trigger on system table",0); + pParse->nErr++; + goto trigger_cleanup; + } + if( tab->pSelect && tr_tm != TK_INSTEAD ){ + sqliteSetNString(&pParse->zErrMsg, "cannot create ", + (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", " trigger on view: ", + pTableName->a[0].zName, 0); + goto trigger_cleanup; + } + if( !tab->pSelect && tr_tm == TK_INSTEAD ){ + sqliteSetNString(&pParse->zErrMsg, "cannot create INSTEAD OF", + " trigger on table: ", pTableName->a[0].zName); + goto trigger_cleanup; + } +#ifndef SQLITE_OMIT_AUTHORIZATION { - char *tmp_str = sqliteStrNDup(pTableName->z, pTableName->n); - if( tmp_str==0 ) goto trigger_cleanup; - tab = sqliteFindTable(pParse->db, tmp_str); - sqliteFree(tmp_str); - if( !tab ){ - sqliteSetNString(&pParse->zErrMsg, "no such table: ", -1, - pTableName->z, pTableName->n, 0); - pParse->nErr++; - goto trigger_cleanup; - } - if( sqliteStrICmp(tab->zName, MASTER_NAME)==0 ){ - sqliteSetString(&pParse->zErrMsg, "cannot create trigger on system " - "table: " MASTER_NAME, 0); - pParse->nErr++; - goto trigger_cleanup; - } - if( sqliteStrICmp(tab->zName, TEMP_MASTER_NAME)==0 ){ - sqliteSetString(&pParse->zErrMsg, "cannot create trigger on system " - "table: " TEMP_MASTER_NAME, 0); - pParse->nErr++; + int code = SQLITE_CREATE_TRIGGER; + if( tab->iDb==1 ) code = SQLITE_CREATE_TEMP_TRIGGER; + if( sqliteAuthCheck(pParse, code, zName, tab->zName) ){ goto trigger_cleanup; } - if( tab->pSelect && tr_tm != TK_INSTEAD ){ - sqliteSetNString(&pParse->zErrMsg, "cannot create ", -1, - (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", -1, " trigger on view: ", -1 - , pTableName->z, pTableName->n, 0); + if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->iDb), 0)){ goto trigger_cleanup; } - if( !tab->pSelect && tr_tm == TK_INSTEAD ){ - sqliteSetNString(&pParse->zErrMsg, "cannot create INSTEAD OF", -1, - " trigger on table: ", -1, pTableName->z, pTableName->n, 0); - goto trigger_cleanup; - } -#ifndef SQLITE_OMIT_AUTHORIZATION - { - int code = SQLITE_CREATE_TRIGGER; - if( tab->isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER; - if( sqliteAuthCheck(pParse, code, zName, tab->zName) ){ - goto trigger_cleanup; - } - if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->isTemp), 0)){ - goto trigger_cleanup; - } - } -#endif } +#endif if (tr_tm == TK_INSTEAD){ tr_tm = TK_BEFORE; @@ -122,8 +118,9 @@ void sqliteCreateTrigger( if( nt==0 ) goto trigger_cleanup; nt->name = zName; zName = 0; - nt->table = sqliteStrNDup(pTableName->z, pTableName->n); + nt->table = sqliteStrDup(pTableName->a[0].zName); if( sqlite_malloc_failed ) goto trigger_cleanup; + nt->iDb = tab->iDb; nt->op = op; nt->tr_tm = tr_tm; nt->pWhen = sqliteExprDup(pWhen); @@ -154,15 +151,15 @@ void sqliteCreateTrigger( v = sqliteGetVdbe(pParse); if( v==0 ) goto trigger_cleanup; sqliteBeginWriteOperation(pParse, 0, 0); - sqliteOpenMasterTable(v, tab->isTemp); + sqliteOpenMasterTable(v, tab->iDb); addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig); - sqliteVdbeChangeP3(v, addr, tab->isTemp ? TEMP_MASTER_NAME : MASTER_NAME, + sqliteVdbeChangeP3(v, addr, tab->iDb ? TEMP_MASTER_NAME : MASTER_NAME, P3_STATIC); sqliteVdbeChangeP3(v, addr+2, nt->name, 0); sqliteVdbeChangeP3(v, addr+3, nt->table, 0); sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n); - if( !tab->isTemp ){ - sqliteChangeCookie(pParse->db, v); + if( tab->iDb==0 ){ + sqliteChangeCookie(db, v); } sqliteVdbeAddOp(v, OP_Close, 0, 0); sqliteEndWriteOperation(pParse); @@ -170,7 +167,7 @@ void sqliteCreateTrigger( if( !pParse->explain ){ /* Stick it in the hash-table */ - sqliteHashInsert(&(pParse->db->trigHash), nt->name, pName->n + 1, nt); + sqliteHashInsert(&(db->aDb[nt->iDb].trigHash), nt->name, pName->n + 1, nt); /* Attach it to the table object */ nt->pNext = tab->pTrigger; @@ -185,6 +182,7 @@ void sqliteCreateTrigger( trigger_cleanup: sqliteFree(zName); + sqliteSrcListDelete(pTableName); sqliteIdListDelete(pColumns); sqliteExprDelete(pWhen); sqliteDeleteTriggerStep(pStepList); @@ -342,31 +340,46 @@ void sqliteDeleteTrigger(Trigger *pTrigger){ * table. This is so that the trigger can be restored into the database schema * if the transaction is rolled back. */ -void sqliteDropTrigger(Parse *pParse, Token *pName, int nested){ - char *zName; +void sqliteDropTrigger(Parse *pParse, SrcList *pName, int nested){ Trigger *pTrigger; Table *pTable; Vdbe *v; - - zName = sqliteStrNDup(pName->z, pName->n); - - /* ensure that the trigger being dropped exists */ - pTrigger = sqliteHashFind(&(pParse->db->trigHash), zName, pName->n + 1); + int i; + const char *zDb; + const char *zName; + int nName; + sqlite *db = pParse->db; + + if( sqlite_malloc_failed ) goto drop_trigger_cleanup; + assert( pName->nSrc==1 ); + zDb = pName->a[0].zDatabase; + zName = pName->a[0].zName; + nName = strlen(zName); + for(i=0; i<db->nDb; i++){ + if( zDb && sqliteStrICmp(db->aDb[i].zName, zDb) ) continue; + pTrigger = sqliteHashFind(&(db->aDb[i].trigHash), zName, nName+1); + if( pTrigger ) break; + } if( !pTrigger ){ - sqliteSetNString(&pParse->zErrMsg, "no such trigger: ", -1, - zName, -1, 0); - sqliteFree(zName); - return; + sqliteSetString(&pParse->zErrMsg, "no such trigger: ", zName, 0); + goto drop_trigger_cleanup; + } + assert( pTrigger->iDb>=0 && pTrigger->iDb<db->nDb ); + if( pTrigger->iDb>=2 ){ + sqliteSetString(&pParse->zErrMsg, "triggers may not be removed from " + "auxiliary database \"", db->aDb[pTrigger->iDb].zName, "\"", 0); + pParse->nErr++; + goto drop_trigger_cleanup; } - pTable = sqliteFindTable(pParse->db, pTrigger->table); + pTable = sqliteFindTable(db, pTrigger->table, db->aDb[pTrigger->iDb].zName); assert(pTable); + assert( pTable->iDb==pTrigger->iDb ); #ifndef SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_DROP_TRIGGER; - if( pTable->isTemp ) code = SQLITE_DROP_TEMP_TRIGGER; + if( pTable->iDb ) code = SQLITE_DROP_TEMP_TRIGGER; if( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName) || - sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTable->isTemp),0) ){ - sqliteFree(zName); + sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTable->iDb),0) ){ return; } } @@ -389,7 +402,7 @@ void sqliteDropTrigger(Parse *pParse, Token *pName, int nested){ } assert(cc); } - sqliteHashInsert(&(pParse->db->trigHash), zName, pName->n + 1, NULL); + sqliteHashInsert(&(db->aDb[pTrigger->iDb].trigHash), zName, nName+1, 0); sqliteDeleteTrigger(pTrigger); } @@ -409,17 +422,18 @@ void sqliteDropTrigger(Parse *pParse, Token *pName, int nested){ }; sqliteBeginWriteOperation(pParse, 0, 0); - sqliteOpenMasterTable(v, pTable->isTemp); + sqliteOpenMasterTable(v, pTable->iDb); base = sqliteVdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); sqliteVdbeChangeP3(v, base+1, zName, 0); - if( !pTable->isTemp ){ - sqliteChangeCookie(pParse->db, v); + if( pTable->iDb==0 ){ + sqliteChangeCookie(db, v); } sqliteVdbeAddOp(v, OP_Close, 0, 0); sqliteEndWriteOperation(pParse); } - sqliteFree(zName); +drop_trigger_cleanup: + sqliteSrcListDelete(pName); } /* diff --git a/src/update.c b/src/update.c index 93a67448c..f76686d53 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.55 2003/03/20 01:16:59 drh Exp $ +** $Id: update.c,v 1.56 2003/03/27 12:51:25 drh Exp $ */ #include "sqliteInt.h" @@ -28,6 +28,7 @@ void sqliteUpdate( ){ int i, j; /* Loop counters */ char *zTab; /* Name of the table to be updated */ + char *zDb; /* Name of the database holding zTab */ Table *pTab; /* The table to be updated */ int addr; /* VDBE instruction address of the start of the loop */ WhereInfo *pWInfo; /* Information about the WHERE clause */ @@ -59,8 +60,9 @@ void sqliteUpdate( * defined */ zTab = pTabList->a[0].zName; + zDb = pTabList->a[0].zDatabase; if( zTab != 0 ){ - pTab = sqliteFindTable(pParse->db, zTab); + pTab = sqliteFindTable(pParse->db, zTab, zDb); if( pTab ){ row_triggers_exist = sqliteTriggersExist(pParse, pTab->pTrigger, @@ -82,7 +84,7 @@ void sqliteUpdate( ** will be calling are designed to work with multiple tables and expect ** an SrcList* parameter instead of just a Table* parameter. */ - pTab = pTabList->a[0].pTab = sqliteTableNameToTable(pParse, zTab); + pTab = pTabList->a[0].pTab = sqliteTableNameToTable(pParse, zTab, zDb); if( pTab==0 ) goto update_cleanup; assert( pTab->pSelect==0 ); /* This table is not a VIEW */ aXRef = sqliteMalloc( sizeof(int) * pTab->nCol ); @@ -197,7 +199,7 @@ void sqliteUpdate( */ v = sqliteGetVdbe(pParse); if( v==0 ) goto update_cleanup; - sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->isTemp); + sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->iDb==1); /* Begin the database scan */ @@ -229,7 +231,7 @@ void sqliteUpdate( sqliteVdbeAddOp(v, OP_Dup, 0, 0); sqliteVdbeAddOp(v, OP_Dup, 0, 0); - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); @@ -275,7 +277,7 @@ void sqliteUpdate( ** action, then we need to open all indices because we might need ** to be deleting some records. */ - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum); if( onError==OE_Replace ){ openAll = 1; @@ -290,7 +292,7 @@ void sqliteUpdate( } for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ if( openAll || aIdxUsed[i] ){ - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); sqliteVdbeAddOp(v, OP_OpenWrite, base+i+1, pIdx->tnum); assert( pParse->nTab>base+i+1 ); } diff --git a/src/where.c b/src/where.c index 3f7f306a0..66b13b8be 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.73 2003/03/19 03:14:03 drh Exp $ +** $Id: where.c,v 1.74 2003/03/27 12:51:26 drh Exp $ */ #include "sqliteInt.h" @@ -640,7 +640,7 @@ WhereInfo *sqliteWhereBegin( pTab = pTabList->a[i].pTab; if( pTab->isTransient || pTab->pSelect ) continue; - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, base+i, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); if( i==0 && !pParse->schemaVerified && @@ -648,7 +648,7 @@ WhereInfo *sqliteWhereBegin( sqliteCodeVerifySchema(pParse); } if( pWInfo->a[i].pIdx!=0 ){ - sqliteVdbeAddOp(v, OP_Integer, pTab->isTemp, 0); + sqliteVdbeAddOp(v, OP_Integer, pWInfo->a[i].pIdx->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, pWInfo->a[i].iCur, pWInfo->a[i].pIdx->tnum); sqliteVdbeChangeP3(v, -1, pWInfo->a[i].pIdx->zName, P3_STATIC); |