diff options
author | Wez Furlong <wez@php.net> | 2003-06-04 22:40:00 +0000 |
---|---|---|
committer | Wez Furlong <wez@php.net> | 2003-06-04 22:40:00 +0000 |
commit | 80e7f7001d39add9010ba78be636245410b79c24 (patch) | |
tree | 23ae7f9f01ea9bb1add35b1ff85efa2421d05142 /ext/sqlite/libsqlite/src/build.c | |
parent | 82a1818fdec3afe8e3a5cc8aa7171f4472ea1e4a (diff) | |
download | php-git-80e7f7001d39add9010ba78be636245410b79c24.tar.gz |
Update bundled library to version 2.8.2.
Make OnUpdateInt compatible with ZE2.
Fix the makefile fragment for non-gnu makes
Diffstat (limited to 'ext/sqlite/libsqlite/src/build.c')
-rw-r--r-- | ext/sqlite/libsqlite/src/build.c | 1219 |
1 files changed, 429 insertions, 790 deletions
diff --git a/ext/sqlite/libsqlite/src/build.c b/ext/sqlite/libsqlite/src/build.c index afa3f16025..76077ac943 100644 --- a/ext/sqlite/libsqlite/src/build.c +++ b/ext/sqlite/libsqlite/src/build.c @@ -18,8 +18,6 @@ ** CREATE INDEX ** DROP INDEX ** creating ID lists -** COPY -** VACUUM ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK @@ -38,6 +36,7 @@ */ void sqliteBeginParse(Parse *pParse, int explainFlag){ sqlite *db = pParse->db; + int i; pParse->explain = explainFlag; if((db->flags & SQLITE_Initialized)==0 && pParse->initFlag==0 ){ int rc = sqliteInit(db, &pParse->zErrMsg); @@ -46,6 +45,12 @@ void sqliteBeginParse(Parse *pParse, int explainFlag){ pParse->nErr++; } } + for(i=0; i<db->nDb; i++){ + DbClearProperty(db, i, DB_Locked); + if( !db->aDb[i].inTrans ){ + DbClearProperty(db, i, DB_Cookie); + } + } } /* @@ -85,7 +90,7 @@ void sqliteExec(Parse *pParse){ if( pParse->useCallback ){ if( pParse->explain ){ rc = sqliteVdbeList(v); - db->next_cookie = db->schema_cookie; + db->next_cookie = db->aDb[0].schema_cookie; }else{ sqliteVdbeExec(v); } @@ -98,7 +103,6 @@ void sqliteExec(Parse *pParse){ pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE; } pParse->colNamesSet = 0; - pParse->schemaVerified = 0; }else if( pParse->useCallback==0 ){ pParse->rc = SQLITE_ERROR; } @@ -111,22 +115,82 @@ void sqliteExec(Parse *pParse){ /* ** Locate the in-memory structure that describes ** a particular database table given the name -** of that table. Return NULL if not found. +** of that table and (optionally) the name of the database +** containing the table. Return NULL if not found. +** +** See also sqliteLocateTable(). */ -Table *sqliteFindTable(sqlite *db, const char *zName){ +Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){ + Table *p = 0; + int i; + for(i=0; i<db->nDb; i++){ + int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ + if( zDatabase!=0 && sqliteStrICmp(zDatabase, db->aDb[j].zName) ) continue; + p = sqliteHashFind(&db->aDb[j].tblHash, zName, strlen(zName)+1); + if( p ) break; + } + return p; +} + +/* +** Locate the in-memory structure that describes +** a particular database table given the name +** of that table and (optionally) the name of the database +** containing the table. Return NULL if not found. +** +** If pParse->useDb is not negative, then the table must be +** located in that database. If a different database is specified, +** an error message is generated into pParse->zErrMsg. +*/ +Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){ + sqlite *db; + const char *zUse; Table *p; - p = sqliteHashFind(&db->tblHash, zName, strlen(zName)+1); + db = pParse->db; + if( pParse->useDb<0 ){ + p = sqliteFindTable(db, zName, zDbase); + }else { + assert( pParse->useDb<db->nDb ); + assert( db->aDb[pParse->useDb].pBt!=0 ); + zUse = db->aDb[pParse->useDb].zName; + if( zDbase && pParse->useDb!=1 && sqliteStrICmp(zDbase, zUse)!=0 ){ + sqliteErrorMsg(pParse,"cannot use database %s in this context", zDbase); + return 0; + } + p = sqliteFindTable(db, zName, zUse); + if( p==0 && pParse->useDb==1 && zDbase==0 ){ + p = sqliteFindTable(db, zName, 0); + } + } + if( p==0 ){ + if( zDbase ){ + sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName); + }else if( (pParse->useDb==0 || pParse->useDb>=2) + && sqliteFindTable(db, zName, 0)!=0 ){ + sqliteErrorMsg(pParse, "table \"%s\" is not in database \"%s\"", + zName, zUse); + }else{ + sqliteErrorMsg(pParse, "no such table: %s", zName); + } + } return p; } /* ** Locate the in-memory structure that describes -** a particular index given the name of that index. +** a particular index given the name of that index +** and the name of the database that contains the 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++){ + int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ + if( zDb && sqliteStrICmp(zDb, db->aDb[j].zName) ) continue; + p = sqliteHashFind(&db->aDb[j].idxHash, zName, strlen(zName)+1); + if( p ) break; + } return p; } @@ -140,10 +204,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); } @@ -171,29 +238,67 @@ void sqliteUnlinkAndDeleteIndex(sqlite *db, Index *pIndex){ ** database connection. This routine is called to reclaim memory ** before the connection closes. It is also called during a rollback ** if there were schema changes during the transaction. +** +** If iDb<=0 then reset the internal schema tables for all database +** files. If iDb>=2 then reset the internal schema for only the +** single file indicates. */ -void sqliteResetInternalSchema(sqlite *db){ +void sqliteResetInternalSchema(sqlite *db, int iDb){ HashElem *pElem; Hash temp1; Hash temp2; + int i, j; + + assert( iDb>=0 && iDb<db->nDb ); + db->flags &= ~SQLITE_Initialized; + for(i=iDb; 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); + DbClearProperty(db, i, DB_SchemaLoaded); + if( iDb>0 ) return; + } + assert( iDb==0 ); + db->flags &= ~SQLITE_InternChanges; - 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); - db->flags &= ~(SQLITE_Initialized|SQLITE_InternChanges); + /* If one or more of the auxiliary database files has been closed, + ** then remove then from the auxiliary database list. We take the + ** opportunity to do this here since we have just deleted all of the + ** schema hash tables and therefore do not have to make any changes + ** to any of those tables. + */ + for(i=j=2; i<db->nDb; i++){ + if( db->aDb[i].pBt==0 ){ + sqliteFree(db->aDb[i].zName); + db->aDb[i].zName = 0; + continue; + } + if( j<i ){ + db->aDb[j] = db->aDb[i]; + } + j++; + } + memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j])); + db->nDb = j; + if( db->nDb<=2 && db->aDb!=db->aDbStatic ){ + memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0])); + sqliteFree(db->aDb); + db->aDb = db->aDbStatic; + } } /* @@ -203,7 +308,7 @@ void sqliteResetInternalSchema(sqlite *db){ */ void sqliteRollbackInternalChanges(sqlite *db){ if( db->flags & SQLITE_InternChanges ){ - sqliteResetInternalSchema(db); + sqliteResetInternalSchema(db, 0); } } @@ -211,7 +316,7 @@ void sqliteRollbackInternalChanges(sqlite *db){ ** This routine is called when a commit occurs. */ void sqliteCommitInternalChanges(sqlite *db){ - db->schema_cookie = db->next_cookie; + db->aDb[0].schema_cookie = db->next_cookie; db->flags &= ~SQLITE_InternChanges; } @@ -241,6 +346,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 +355,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 +381,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 ){ @@ -310,13 +419,8 @@ char *sqliteTableNameFromToken(Token *pName){ ** on cursor 0. */ void sqliteOpenMasterTable(Vdbe *v, int isTemp){ - if( isTemp ){ - sqliteVdbeAddOp(v, OP_OpenWrAux, 0, 2); - sqliteVdbeChangeP3(v, -1, TEMP_MASTER_NAME, P3_STATIC); - }else{ - sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2); - sqliteVdbeChangeP3(v, -1, MASTER_NAME, P3_STATIC); - } + sqliteVdbeAddOp(v, OP_Integer, isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2); } /* @@ -348,17 +452,21 @@ void sqliteStartTable( char *zName; sqlite *db = pParse->db; Vdbe *v; + int iDb; pParse->sFirstToken = *pStart; zName = sqliteTableNameFromToken(pName); if( zName==0 ) return; + if( pParse->iDb==1 ) isTemp = 1; #ifndef SQLITE_OMIT_AUTHORIZATION - if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0) ){ - sqliteFree(zName); - return; - } + assert( (isTemp & 1)==isTemp ); { int code; + char *zDb = isTemp ? "temp" : "main"; + if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){ + sqliteFree(zName); + return; + } if( isView ){ if( isTemp ){ code = SQLITE_CREATE_TEMP_VIEW; @@ -372,7 +480,7 @@ void sqliteStartTable( code = SQLITE_CREATE_TABLE; } } - if( sqliteAuthCheck(pParse, code, zName, 0) ){ + if( sqliteAuthCheck(pParse, code, zName, 0, zDb) ){ sqliteFree(zName); return; } @@ -383,8 +491,8 @@ void sqliteStartTable( /* 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( isTemp && db->aDb[1].pBt==0 && !pParse->explain ){ + int rc = sqliteBtreeFactory(db, 0, 0, MAX_PAGES, &db->aDb[1].pBt); if( rc!=SQLITE_OK ){ sqliteSetString(&pParse->zErrMsg, "unable to open a temporary database " "file for storing temporary tables", 0); @@ -392,7 +500,7 @@ void sqliteStartTable( return; } if( db->flags & SQLITE_InTrans ){ - rc = sqliteBtreeBeginTrans(db->pBeTemp); + rc = sqliteBtreeBeginTrans(db->aDb[1].pBt); if( rc!=SQLITE_OK ){ sqliteSetNString(&pParse->zErrMsg, "unable to get a write lock on " "the temporary database file", 0); @@ -407,26 +515,19 @@ 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); + iDb = isTemp ? 1 : pParse->iDb; + if( pTable!=0 && (pTable->iDb==iDb || !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); @@ -443,7 +544,7 @@ void sqliteStartTable( pTable->aCol = 0; pTable->iPKey = -1; pTable->pIndex = 0; - pTable->isTemp = isTemp; + pTable->iDb = iDb; if( pParse->pNewTable ) sqliteDeleteTable(db, pParse->pNewTable); pParse->pNewTable = pTable; @@ -606,12 +707,12 @@ void sqliteAddPrimaryKey(Parse *pParse, IdList *pList, int onError){ Table *pTab = pParse->pNewTable; char *zType = 0; int iCol = -1; - if( pTab==0 ) return; + if( pTab==0 ) goto primary_key_exit; if( pTab->hasPrimKey ){ sqliteSetString(&pParse->zErrMsg, "table \"", pTab->zName, "\" has more than one primary key", 0); pParse->nErr++; - return; + goto primary_key_exit; } pTab->hasPrimKey = 1; if( pList==0 ){ @@ -629,8 +730,13 @@ 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; } + +primary_key_exit: + sqliteIdListDelete(pList); + return; } /* @@ -708,8 +814,8 @@ void sqliteAddCollateType(Parse *pParse, int collType){ ** 1 chance in 2^32. So we're safe enough. */ void sqliteChangeCookie(sqlite *db, Vdbe *v){ - if( db->next_cookie==db->schema_cookie ){ - db->next_cookie = db->schema_cookie + sqliteRandomByte() + 1; + if( db->next_cookie==db->aDb[0].schema_cookie ){ + db->next_cookie = db->aDb[0].schema_cookie + sqliteRandomByte() + 1; db->flags |= SQLITE_InternChanges; sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0); sqliteVdbeAddOp(v, OP_SetCookie, 0, 0); @@ -778,7 +884,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 +965,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,13 +997,13 @@ 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 ){ - int op = p->isTemp ? OP_OpenWrAux : OP_OpenWrite; - sqliteVdbeAddOp(v, op, 1, 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 +1012,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 +1144,7 @@ int sqliteViewGetColumnNames(Parse *pParse, Table *pTable){ pSelTab->nCol = 0; pSelTab->aCol = 0; sqliteDeleteTable(0, pSelTab); - pParse->db->flags |= SQLITE_UnresetViews; + DbSetProperty(pParse->db, pTable->iDb, DB_UnresetViews); }else{ pTable->nCol = 0; nErr++; @@ -1072,18 +1178,18 @@ static void sqliteViewResetColumnNames(Table *pTable){ } /* -** Clear the column names from every VIEW. +** Clear the column names from every VIEW in database idx. */ -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( !DbHasProperty(db, idx, DB_UnresetViews) ) 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; + DbClearProperty(db, idx, DB_UnresetViews); } /* @@ -1095,7 +1201,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,33 +1220,38 @@ 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; + assert( iDb>=0 && iDb<db->nDb ); #ifndef SQLITE_OMIT_AUTHORIZATION - if( sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTable->isTemp),0)){ - return; - } { int code; + const char *zTab = SCHEMA_TABLE(pTable->iDb); + const char *zDb = db->aDb[pTable->iDb].zName; + if( sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){ + return; + } 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; } } - if( sqliteAuthCheck(pParse, code, pTable->zName, 0) ){ + if( sqliteAuthCheck(pParse, code, pTable->zName, 0, zDb) ){ return; } - if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTable->zName, 0) ){ + if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTable->zName, 0, zDb) ){ return; } } @@ -1181,31 +1292,44 @@ 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); + /* 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 || pTrigger->iDb==1 ); + 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{ pTrigger = pTable->pTrigger; } } + + /* Drop all SQLITE_MASTER entries that refer to the table */ + sqliteOpenMasterTable(v, pTable->iDb); base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); sqliteVdbeChangeP3(v, base+1, pTable->zName, 0); - if( !pTable->isTemp ){ + + /* Drop all SQLITE_TEMP_MASTER entries that refer to the table */ + if( pTable->iDb!=1 ){ + sqliteOpenMasterTable(v, 1); + base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); + sqliteVdbeChangeP3(v, base+1, pTable->zName, 0); + } + + if( pTable->iDb==0 ){ 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, pIdx->iDb); } } sqliteEndWriteOperation(pParse); @@ -1220,7 +1344,7 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){ sqliteUnlinkAndDeleteTable(db, pTable); db->flags |= SQLITE_InternChanges; } - sqliteViewResetAll(db); + sqliteViewResetAll(db, iDb); } /* @@ -1403,9 +1527,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 +1540,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 +1548,22 @@ void sqliteCreateIndex( */ if( pTable!=0 ){ assert( pName!=0 ); - pTab = sqliteTableFromToken(pParse, pTable); + assert( pTable->nSrc==1 ); + pTab = sqliteSrcListLookup(pParse, pTable); }else{ assert( pName==0 ); pTab = pParse->pNewTable; } if( pTab==0 || pParse->nErr ) goto exit_create_index; if( pTab->readOnly ){ + sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName, + " may not be indexed", 0); + pParse->nErr++; + goto exit_create_index; + } + if( !isTemp && pTab->iDb>=2 && pParse->initFlag==0 ){ 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,16 +1572,8 @@ void sqliteCreateIndex( pParse->nErr++; 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 that 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 && !pParse->isTemp && pTab->isTemp ){ - goto exit_create_index; + if( pTab->iDb==1 ){ + isTemp = 1; } /* @@ -1460,40 +1583,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,19 +1615,26 @@ 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) ){ - goto exit_create_index; - } - i = SQLITE_CREATE_INDEX; - if( pTab->isTemp ) i = SQLITE_CREATE_TEMP_INDEX; - if( sqliteAuthCheck(pParse, i, zName, pTab->zName) ){ - goto exit_create_index; + { + const char *zDb = db->aDb[pTab->iDb].zName; + + assert( isTemp==0 || isTemp==1 ); + assert( pTab->iDb==pParse->iDb || isTemp==1 ); + if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){ + goto exit_create_index; + } + i = SQLITE_CREATE_INDEX; + if( isTemp ) i = SQLITE_CREATE_TEMP_INDEX; + if( sqliteAuthCheck(pParse, i, zName, pTab->zName, zDb) ){ + goto exit_create_index; + } } #endif @@ -1540,8 +1660,9 @@ void sqliteCreateIndex( strcpy(pIndex->zName, zName); pIndex->pTable = pTab; pIndex->nColumn = pList->nId; - pIndex->onError = pIndex->isUnique = onError; + pIndex->onError = 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 +1685,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 +1744,6 @@ void sqliteCreateIndex( int lbl1, lbl2; int i; int addr; - int isTemp = pTab->isTemp; v = sqliteGetVdbe(pParse); if( v==0 ) goto exit_create_index; @@ -1642,11 +1763,8 @@ void sqliteCreateIndex( pIndex->tnum = 0; if( pTable ){ sqliteVdbeAddOp(v, OP_Dup, 0, 0); - if( isTemp ){ - sqliteVdbeAddOp(v, OP_OpenWrAux, 1, 0); - }else{ - sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0); - } + sqliteVdbeAddOp(v, OP_Integer, isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0); } addr = sqliteVdbeAddOp(v, OP_String, 0, 0); if( pStart && pEnd ){ @@ -1656,13 +1774,19 @@ void sqliteCreateIndex( sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0); sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0); if( pTable ){ - sqliteVdbeAddOp(v, isTemp ? OP_OpenAux : OP_Open, 2, pTab->tnum); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); + sqliteVdbeAddOp(v, OP_OpenRead, 2, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); lbl2 = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Rewind, 2, lbl2); lbl1 = sqliteVdbeAddOp(v, OP_Recno, 2, 0); for(i=0; i<pIndex->nColumn; i++){ - sqliteVdbeAddOp(v, OP_Column, 2, pIndex->aiColumn[i]); + int iCol = pIndex->aiColumn[i]; + if( pTab->iPKey==iCol ){ + sqliteVdbeAddOp(v, OP_Dup, i, 0); + }else{ + sqliteVdbeAddOp(v, OP_Column, 2, iCol); + } } sqliteVdbeAddOp(v, OP_MakeIdxKey, pIndex->nColumn, 0); if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIndex); @@ -1685,6 +1809,7 @@ void sqliteCreateIndex( /* Clean up before exiting */ exit_create_index: sqliteIdListDelete(pList); + sqliteSrcListDelete(pTable); sqliteFree(zName); return; } @@ -1693,39 +1818,40 @@ 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); - pParse->nErr++; - return; + sqliteErrorMsg(pParse, "no such index: %S", pName, 0); + goto exit_drop_index; } if( pIndex->autoIndex ){ - sqliteSetString(&pParse->zErrMsg, "index associated with UNIQUE " + sqliteErrorMsg(pParse, "index associated with UNIQUE " "or PRIMARY KEY constraint cannot be dropped", 0); - pParse->nErr++; - return; + goto exit_drop_index; + } + if( pIndex->iDb>1 ){ + sqliteErrorMsg(pParse, "cannot alter schema of attached " + "databases", 0); + 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; + const char *zDb = db->aDb[pIndex->iDb].zName; + const char *zTab = SCHEMA_TABLE(pIndex->iDb); + if( sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ + goto exit_drop_index; } - if( pTab->isTemp ) code = SQLITE_DROP_TEMP_INDEX; - if( sqliteAuthCheck(pParse, code, pIndex->zName, pTab->zName) ){ - return; + if( pIndex->iDb ) code = SQLITE_DROP_TEMP_INDEX; + if( sqliteAuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ + goto exit_drop_index; } } #endif @@ -1745,17 +1871,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); } @@ -1765,6 +1890,9 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ sqliteUnlinkAndDeleteIndex(db, pIndex); db->flags |= SQLITE_InternChanges; } + +exit_drop_index: + sqliteSrcListDelete(pName); } /* @@ -1807,25 +1935,53 @@ IdList *sqliteIdListAppend(IdList *pList, Token *pToken){ ** need be. A new entry is created in the SrcList even if pToken is NULL. ** ** A new SrcList is returned, or NULL if malloc() fails. +** +** If pDatabase is not null, it means that the table has an optional +** database name prefix. Like this: "database.table". The pDatabase +** points to the table name and the pTable points to the database name. +** The SrcList.a[].zName field is filled with the table name which might +** come from pTable (if pDatabase is NULL) or from pDatabase. +** SrcList.a[].zDatabase is filled with the database name from pTable, +** or with NULL if no database is specified. +** +** In other words, if call like this: +** +** sqliteSrcListAppend(A,B,0); +** +** Then B is a table name and the database name is unspecified. If called +** like this: +** +** sqliteSrcListAppend(A,B,C); +** +** Then C is the table name and B is the database name. */ -SrcList *sqliteSrcListAppend(SrcList *pList, Token *pToken){ +SrcList *sqliteSrcListAppend(SrcList *pList, Token *pTable, Token *pDatabase){ if( pList==0 ){ - pList = sqliteMalloc( sizeof(IdList) ); + pList = sqliteMalloc( sizeof(SrcList) ); if( pList==0 ) return 0; } - if( (pList->nSrc & 7)==0 ){ - struct SrcList_item *a; - a = sqliteRealloc(pList->a, (pList->nSrc+8)*sizeof(pList->a[0]) ); - if( a==0 ){ + if( (pList->nSrc & 7)==1 ){ + SrcList *pNew; + pNew = sqliteRealloc(pList, + sizeof(*pList) + (pList->nSrc+8)*sizeof(pList->a[0]) ); + if( pNew==0 ){ sqliteSrcListDelete(pList); return 0; } - pList->a = a; + pList = pNew; } memset(&pList->a[pList->nSrc], 0, sizeof(pList->a[0])); - if( pToken ){ + if( pDatabase && pDatabase->z==0 ){ + pDatabase = 0; + } + if( pDatabase && pTable ){ + Token *pTemp = pDatabase; + pDatabase = pTable; + pTable = pTemp; + } + if( pTable ){ char **pz = &pList->a[pList->nSrc].zName; - sqliteSetNString(pz, pToken->z, pToken->n, 0); + sqliteSetNString(pz, pTable->z, pTable->n, 0); if( *pz==0 ){ sqliteSrcListDelete(pList); return 0; @@ -1833,11 +1989,34 @@ SrcList *sqliteSrcListAppend(SrcList *pList, Token *pToken){ sqliteDequote(*pz); } } + if( pDatabase ){ + char **pz = &pList->a[pList->nSrc].zDatabase; + sqliteSetNString(pz, pDatabase->z, pDatabase->n, 0); + if( *pz==0 ){ + sqliteSrcListDelete(pList); + return 0; + }else{ + sqliteDequote(*pz); + } + } + pList->a[pList->nSrc].iCursor = -1; pList->nSrc++; return pList; } /* +** Assign cursors to all tables in a SrcList +*/ +void sqliteSrcListAssignCursors(Parse *pParse, SrcList *pList){ + int i; + for(i=0; i<pList->nSrc; i++){ + if( pList->a[i].iCursor<0 ){ + pList->a[i].iCursor = pParse->nTab++; + } + } +} + +/* ** Add an alias to the last identifier on the given identifier list. */ void sqliteSrcListAddAlias(SrcList *pList, Token *pToken){ @@ -1881,6 +2060,7 @@ void sqliteSrcListDelete(SrcList *pList){ int i; if( pList==0 ) return; for(i=0; i<pList->nSrc; i++){ + sqliteFree(pList->a[i].zDatabase); sqliteFree(pList->a[i].zName); sqliteFree(pList->a[i].zAlias); if( pList->a[i].pTab && pList->a[i].pTab->isTransient ){ @@ -1890,137 +2070,20 @@ void sqliteSrcListDelete(SrcList *pList){ sqliteExprDelete(pList->a[i].pOn); sqliteIdListDelete(pList->a[i].pUsing); } - sqliteFree(pList->a); sqliteFree(pList); } /* -** The COPY command is for compatibility with PostgreSQL and specificially -** for the ability to read the output of pg_dump. The format is as -** follows: -** -** COPY table FROM file [USING DELIMITERS string] -** -** "table" is an existing table name. We will read lines of code from -** file to fill this table with data. File might be "stdin". The optional -** delimiter string identifies the field separators. The default is a tab. -*/ -void sqliteCopy( - Parse *pParse, /* The parser context */ - Token *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; - Index *pIdx; - char *zFile = 0; - sqlite *db = pParse->db; - - - zTab = sqliteTableNameFromToken(pTableName); - if( sqlite_malloc_failed || zTab==0 ) goto copy_cleanup; - pTab = sqliteTableNameToTable(pParse, zTab); - sqliteFree(zTab); - if( pTab==0 ) goto copy_cleanup; - zFile = sqliteStrNDup(pFilename->z, pFilename->n); - sqliteDequote(zFile); - if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, zFile) - || sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile) ){ - goto copy_cleanup; - } - v = sqliteGetVdbe(pParse); - if( v ){ - int openOp; - sqliteBeginWriteOperation(pParse, 1, pTab->isTemp); - addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0); - sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n); - sqliteVdbeDequoteP3(v, addr); - openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; - sqliteVdbeAddOp(v, openOp, 0, pTab->tnum); - sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); - for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - sqliteVdbeAddOp(v, openOp, i, pIdx->tnum); - sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC); - } - if( db->flags & SQLITE_CountRows ){ - sqliteVdbeAddOp(v, OP_Integer, 0, 0); /* Initialize the row count */ - } - end = sqliteVdbeMakeLabel(v); - addr = sqliteVdbeAddOp(v, OP_FileRead, pTab->nCol, end); - if( pDelimiter ){ - sqliteVdbeChangeP3(v, addr, pDelimiter->z, pDelimiter->n); - sqliteVdbeDequoteP3(v, addr); - }else{ - sqliteVdbeChangeP3(v, addr, "\t", 1); - } - if( pTab->iPKey>=0 ){ - sqliteVdbeAddOp(v, OP_FileColumn, pTab->iPKey, 0); - sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0); - }else{ - sqliteVdbeAddOp(v, OP_NewRecno, 0, 0); - } - for(i=0; i<pTab->nCol; i++){ - if( i==pTab->iPKey ){ - /* The integer primary key column is filled with NULL since its - ** value is always pulled from the record number */ - sqliteVdbeAddOp(v, OP_String, 0, 0); - }else{ - sqliteVdbeAddOp(v, OP_FileColumn, i, 0); - } - } - sqliteGenerateConstraintChecks(pParse, pTab, 0, 0, 0, 0, onError, addr); - sqliteCompleteInsertion(pParse, pTab, 0, 0, 0, 0); - if( (db->flags & SQLITE_CountRows)!=0 ){ - sqliteVdbeAddOp(v, OP_AddImm, 1, 0); /* Increment row count */ - } - sqliteVdbeAddOp(v, OP_Goto, 0, addr); - sqliteVdbeResolveLabel(v, end); - sqliteVdbeAddOp(v, OP_Noop, 0, 0); - sqliteEndWriteOperation(pParse); - if( db->flags & SQLITE_CountRows ){ - sqliteVdbeAddOp(v, OP_ColumnName, 0, 0); - sqliteVdbeChangeP3(v, -1, "rows inserted", P3_STATIC); - sqliteVdbeAddOp(v, OP_Callback, 1, 0); - } - } - -copy_cleanup: - sqliteFree(zFile); - return; -} - -/* -** The non-standard VACUUM command is used to clean up the database, -** collapse free space, etc. It is modelled after the VACUUM command -** in PostgreSQL. -** -** In version 1.0.x of SQLite, the VACUUM command would call -** gdbm_reorganize() on all the database tables. But beginning -** with 2.0.0, SQLite no longer uses GDBM so this command has -** become a no-op. -*/ -void sqliteVacuum(Parse *pParse, Token *pTableName){ - /* Do nothing */ -} - -/* ** Begin a transaction */ void sqliteBeginTransaction(Parse *pParse, int onError){ sqlite *db; - if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; if( pParse->nErr || sqlite_malloc_failed ) return; - if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0) ) return; + if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ) return; if( db->flags & SQLITE_InTrans ){ - pParse->nErr++; - sqliteSetString(&pParse->zErrMsg, "cannot start a transaction " - "within a transaction", 0); + sqliteErrorMsg(pParse, "cannot start a transaction within a transaction"); return; } sqliteBeginWriteOperation(pParse, 0, 0); @@ -2034,13 +2097,11 @@ void sqliteBeginTransaction(Parse *pParse, int onError){ void sqliteCommitTransaction(Parse *pParse){ sqlite *db; - if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; if( pParse->nErr || sqlite_malloc_failed ) return; - if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0) ) return; + if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ) return; if( (db->flags & SQLITE_InTrans)==0 ){ - pParse->nErr++; - sqliteSetString(&pParse->zErrMsg, - "cannot commit - no transaction is active", 0); + sqliteErrorMsg(pParse, "cannot commit - no transaction is active"); return; } db->flags &= ~SQLITE_InTrans; @@ -2055,13 +2116,11 @@ void sqliteRollbackTransaction(Parse *pParse){ sqlite *db; Vdbe *v; - if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; if( pParse->nErr || sqlite_malloc_failed ) return; - if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0) ) return; + if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ) return; if( (db->flags & SQLITE_InTrans)==0 ){ - pParse->nErr++; - sqliteSetString(&pParse->zErrMsg, - "cannot rollback - no transaction is active", 0); + sqliteErrorMsg(pParse, "cannot rollback - no transaction is active"); return; } v = sqliteGetVdbe(pParse); @@ -2073,6 +2132,21 @@ void sqliteRollbackTransaction(Parse *pParse){ } /* +** Generate VDBE code that will verify the schema cookie for all +** named database files. +*/ +void sqliteCodeVerifySchema(Parse *pParse, int iDb){ + sqlite *db = pParse->db; + Vdbe *v = sqliteGetVdbe(pParse); + assert( iDb>=0 && iDb<db->nDb ); + assert( db->aDb[iDb].pBt!=0 ); + if( iDb!=1 && !DbHasProperty(db, iDb, DB_Cookie) ){ + sqliteVdbeAddOp(v, OP_VerifyCookie, iDb, db->aDb[iDb].schema_cookie); + DbSetProperty(db, iDb, DB_Cookie); + } +} + +/* ** Generate VDBE code that prepares for doing an operation that ** might change the database. ** @@ -2085,485 +2159,50 @@ void sqliteRollbackTransaction(Parse *pParse){ ** can be checked before any changes are made to the database, it is never ** necessary to undo a write and the checkpoint should not be set. ** -** The tempOnly flag indicates that only temporary tables will be changed -** during this write operation. The primary database table is not -** write-locked. Only the temporary database file gets a write lock. -** Other processes can continue to read or write the primary database file. +** Only database iDb and the temp database are made writable by this call. +** If iDb==0, then the main and temp databases are made writable. If +** iDb==1 then only the temp database is made writable. If iDb>1 then the +** specified auxiliary database and the temp database are made writable. */ -void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int tempOnly){ +void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int iDb){ Vdbe *v; + sqlite *db = pParse->db; + if( DbHasProperty(db, iDb, DB_Locked) ) return; v = sqliteGetVdbe(pParse); if( v==0 ) return; - if( pParse->trigStack ) return; /* if this is in a trigger */ - if( (pParse->db->flags & SQLITE_InTrans)==0 ){ - sqliteVdbeAddOp(v, OP_Transaction, tempOnly, 0); - if( !tempOnly ){ - sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0); - pParse->schemaVerified = 1; + if( !db->aDb[iDb].inTrans ){ + sqliteVdbeAddOp(v, OP_Transaction, iDb, 0); + DbSetProperty(db, iDb, DB_Locked); + sqliteCodeVerifySchema(pParse, iDb); + if( iDb!=1 ){ + sqliteBeginWriteOperation(pParse, setCheckpoint, 1); } }else if( setCheckpoint ){ - sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0); + sqliteVdbeAddOp(v, OP_Checkpoint, iDb, 0); + DbSetProperty(db, iDb, DB_Locked); } } /* ** Generate code that concludes an operation that may have changed -** the database. This is a companion function to BeginWriteOperation(). -** If a transaction was started, then commit it. If a checkpoint was -** started then commit that. +** the database. If a statement transaction was started, then emit +** an OP_Commit that will cause the changes to be committed to disk. +** +** Note that checkpoints are automatically committed at the end of +** a statement. Note also that there can be multiple calls to +** sqliteBeginWriteOperation() but there should only be a single +** call to sqliteEndWriteOperation() at the conclusion of the statement. */ void sqliteEndWriteOperation(Parse *pParse){ Vdbe *v; + sqlite *db = pParse->db; if( pParse->trigStack ) return; /* if this is in a trigger */ v = sqliteGetVdbe(pParse); if( v==0 ) return; - if( pParse->db->flags & SQLITE_InTrans ){ - /* Do Nothing */ + if( db->flags & SQLITE_InTrans ){ + /* A BEGIN has executed. Do not commit until we see an explicit + ** COMMIT statement. */ }else{ sqliteVdbeAddOp(v, OP_Commit, 0, 0); } } - - -/* -** Interpret the given string as a boolean value. -*/ -static int getBoolean(char *z){ - static char *azTrue[] = { "yes", "on", "true" }; - int i; - if( z[0]==0 ) return 0; - if( isdigit(z[0]) || (z[0]=='-' && isdigit(z[1])) ){ - return atoi(z); - } - for(i=0; i<sizeof(azTrue)/sizeof(azTrue[0]); i++){ - if( sqliteStrICmp(z,azTrue[i])==0 ) return 1; - } - return 0; -} - -/* -** Interpret the given string as a safety level. Return 0 for OFF, -** 1 for ON or NORMAL and 2 for FULL. -** -** Note that the values returned are one less that the values that -** should be passed into sqliteBtreeSetSafetyLevel(). The is done -** to support legacy SQL code. The safety level used to be boolean -** and older scripts may have used numbers 0 for OFF and 1 for ON. -*/ -static int getSafetyLevel(char *z){ - static const struct { - const char *zWord; - int val; - } aKey[] = { - { "no", 0 }, - { "off", 0 }, - { "false", 0 }, - { "yes", 1 }, - { "on", 1 }, - { "true", 1 }, - { "full", 2 }, - }; - int i; - if( z[0]==0 ) return 1; - if( isdigit(z[0]) || (z[0]=='-' && isdigit(z[1])) ){ - return atoi(z); - } - for(i=0; i<sizeof(aKey)/sizeof(aKey[0]); i++){ - if( sqliteStrICmp(z,aKey[i].zWord)==0 ) return aKey[i].val; - } - return 1; -} - -/* -** Process a pragma statement. -** -** Pragmas are of this form: -** -** PRAGMA id = value -** -** The identifier might also be a string. The value is a string, and -** identifier, or a number. If minusFlag is true, then the value is -** a number that was preceded by a minus sign. -*/ -void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ - char *zLeft = 0; - char *zRight = 0; - sqlite *db = pParse->db; - - zLeft = sqliteStrNDup(pLeft->z, pLeft->n); - sqliteDequote(zLeft); - if( minusFlag ){ - zRight = 0; - sqliteSetNString(&zRight, "-", 1, pRight->z, pRight->n, 0); - }else{ - zRight = sqliteStrNDup(pRight->z, pRight->n); - sqliteDequote(zRight); - } - if( sqliteAuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight) ){ - sqliteFree(zLeft); - sqliteFree(zRight); - return; - } - - /* - ** PRAGMA default_cache_size - ** PRAGMA default_cache_size=N - ** - ** The first form reports the current persistent setting for the - ** page cache size. The value returned is the maximum number of - ** pages in the page cache. The second form sets both the current - ** page cache size value and the persistent page cache size value - ** stored in the database file. - ** - ** The default cache size is stored in meta-value 2 of page 1 of the - ** database file. The cache size is actually the absolute value of - ** this memory location. The sign of meta-value 2 determines the - ** synchronous setting. A negative value means synchronous is off - ** and a positive value means synchronous is on. - */ - if( sqliteStrICmp(zLeft,"default_cache_size")==0 ){ - static VdbeOp getCacheSize[] = { - { OP_ReadCookie, 0, 2, 0}, - { OP_AbsValue, 0, 0, 0}, - { OP_Dup, 0, 0, 0}, - { OP_Integer, 0, 0, 0}, - { OP_Ne, 0, 6, 0}, - { OP_Integer, MAX_PAGES,0, 0}, - { OP_ColumnName, 0, 0, "cache_size"}, - { OP_Callback, 1, 0, 0}, - }; - Vdbe *v = sqliteGetVdbe(pParse); - if( v==0 ) return; - if( pRight->z==pLeft->z ){ - sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); - }else{ - int addr; - int size = atoi(zRight); - if( size<0 ) size = -size; - sqliteBeginWriteOperation(pParse, 0, 0); - sqliteVdbeAddOp(v, OP_Integer, size, 0); - sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2); - addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0); - sqliteVdbeAddOp(v, OP_Ge, 0, addr+3); - sqliteVdbeAddOp(v, OP_Negative, 0, 0); - sqliteVdbeAddOp(v, OP_SetCookie, 0, 2); - sqliteEndWriteOperation(pParse); - db->cache_size = db->cache_size<0 ? -size : size; - sqliteBtreeSetCacheSize(db->pBe, db->cache_size); - } - }else - - /* - ** PRAGMA cache_size - ** PRAGMA cache_size=N - ** - ** The first form reports the current local setting for the - ** page cache size. The local setting can be different from - ** the persistent cache size value that is stored in the database - ** file itself. The value returned is the maximum number of - ** pages in the page cache. The second form sets the local - ** page cache size value. It does not change the persistent - ** cache size stored on the disk so the cache size will revert - ** to its default value when the database is closed and reopened. - ** N should be a positive integer. - */ - if( sqliteStrICmp(zLeft,"cache_size")==0 ){ - static VdbeOp getCacheSize[] = { - { OP_ColumnName, 0, 0, "cache_size"}, - { OP_Callback, 1, 0, 0}, - }; - Vdbe *v = sqliteGetVdbe(pParse); - if( v==0 ) return; - if( pRight->z==pLeft->z ){ - int size = db->cache_size;; - if( size<0 ) size = -size; - sqliteVdbeAddOp(v, OP_Integer, size, 0); - sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); - }else{ - int size = atoi(zRight); - if( size<0 ) size = -size; - if( db->cache_size<0 ) size = -size; - db->cache_size = size; - sqliteBtreeSetCacheSize(db->pBe, db->cache_size); - } - }else - - /* - ** PRAGMA default_synchronous - ** PRAGMA default_synchronous=ON|OFF|NORMAL|FULL - ** - ** The first form returns the persistent value of the "synchronous" setting - ** that is stored in the database. This is the synchronous setting that - ** is used whenever the database is opened unless overridden by a separate - ** "synchronous" pragma. The second form changes the persistent and the - ** local synchronous setting to the value given. - ** - ** If synchronous is OFF, SQLite does not attempt any fsync() systems calls - ** to make sure data is committed to disk. Write operations are very fast, - ** but a power failure can leave the database in an inconsistent state. - ** If synchronous is ON or NORMAL, SQLite will do an fsync() system call to - ** make sure data is being written to disk. The risk of corruption due to - ** a power loss in this mode is negligible but non-zero. If synchronous - ** is FULL, extra fsync()s occur to reduce the risk of corruption to near - ** zero, but with a write performance penalty. The default mode is NORMAL. - */ - if( sqliteStrICmp(zLeft,"default_synchronous")==0 ){ - static VdbeOp getSync[] = { - { OP_ColumnName, 0, 0, "synchronous"}, - { OP_ReadCookie, 0, 3, 0}, - { OP_Dup, 0, 0, 0}, - { OP_If, 0, 0, 0}, /* 3 */ - { OP_ReadCookie, 0, 2, 0}, - { OP_Integer, 0, 0, 0}, - { OP_Lt, 0, 5, 0}, - { OP_AddImm, 1, 0, 0}, - { OP_Callback, 1, 0, 0}, - { OP_Halt, 0, 0, 0}, - { OP_AddImm, -1, 0, 0}, /* 10 */ - { OP_Callback, 1, 0, 0} - }; - Vdbe *v = sqliteGetVdbe(pParse); - if( v==0 ) return; - if( pRight->z==pLeft->z ){ - int addr = sqliteVdbeAddOpList(v, ArraySize(getSync), getSync); - sqliteVdbeChangeP2(v, addr+3, addr+10); - }else{ - int addr; - int size = db->cache_size; - if( size<0 ) size = -size; - sqliteBeginWriteOperation(pParse, 0, 0); - sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2); - sqliteVdbeAddOp(v, OP_Dup, 0, 0); - addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0); - sqliteVdbeAddOp(v, OP_Ne, 0, addr+3); - sqliteVdbeAddOp(v, OP_AddImm, MAX_PAGES, 0); - sqliteVdbeAddOp(v, OP_AbsValue, 0, 0); - db->safety_level = getSafetyLevel(zRight)+1; - if( db->safety_level==1 ){ - sqliteVdbeAddOp(v, OP_Negative, 0, 0); - size = -size; - } - sqliteVdbeAddOp(v, OP_SetCookie, 0, 2); - sqliteVdbeAddOp(v, OP_Integer, db->safety_level, 0); - sqliteVdbeAddOp(v, OP_SetCookie, 0, 3); - sqliteEndWriteOperation(pParse); - db->cache_size = size; - sqliteBtreeSetCacheSize(db->pBe, db->cache_size); - sqliteBtreeSetSafetyLevel(db->pBe, db->safety_level); - } - }else - - /* - ** PRAGMA synchronous - ** PRAGMA synchronous=OFF|ON|NORMAL|FULL - ** - ** Return or set the local value of the synchronous flag. Changing - ** the local value does not make changes to the disk file and the - ** default value will be restored the next time the database is - ** opened. - */ - if( sqliteStrICmp(zLeft,"synchronous")==0 ){ - static VdbeOp getSync[] = { - { OP_ColumnName, 0, 0, "synchronous"}, - { OP_Callback, 1, 0, 0}, - }; - Vdbe *v = sqliteGetVdbe(pParse); - if( v==0 ) return; - if( pRight->z==pLeft->z ){ - sqliteVdbeAddOp(v, OP_Integer, db->safety_level-1, 0); - sqliteVdbeAddOpList(v, ArraySize(getSync), getSync); - }else{ - int size = db->cache_size; - if( size<0 ) size = -size; - db->safety_level = getSafetyLevel(zRight)+1; - if( db->safety_level==1 ) size = -size; - db->cache_size = size; - sqliteBtreeSetCacheSize(db->pBe, db->cache_size); - sqliteBtreeSetSafetyLevel(db->pBe, db->safety_level); - } - }else - - if( sqliteStrICmp(zLeft, "trigger_overhead_test")==0 ){ - if( getBoolean(zRight) ){ - always_code_trigger_setup = 1; - }else{ - always_code_trigger_setup = 0; - } - }else - - if( sqliteStrICmp(zLeft, "vdbe_trace")==0 ){ - if( getBoolean(zRight) ){ - db->flags |= SQLITE_VdbeTrace; - }else{ - db->flags &= ~SQLITE_VdbeTrace; - } - }else - - if( sqliteStrICmp(zLeft, "full_column_names")==0 ){ - if( getBoolean(zRight) ){ - db->flags |= SQLITE_FullColNames; - }else{ - db->flags &= ~SQLITE_FullColNames; - } - }else - - if( sqliteStrICmp(zLeft, "show_datatypes")==0 ){ - if( getBoolean(zRight) ){ - db->flags |= SQLITE_ReportTypes; - }else{ - db->flags &= ~SQLITE_ReportTypes; - } - }else - - if( sqliteStrICmp(zLeft, "result_set_details")==0 ){ - if( getBoolean(zRight) ){ - db->flags |= SQLITE_ResultDetails; - }else{ - db->flags &= ~SQLITE_ResultDetails; - } - }else - - if( sqliteStrICmp(zLeft, "count_changes")==0 ){ - if( getBoolean(zRight) ){ - db->flags |= SQLITE_CountRows; - }else{ - db->flags &= ~SQLITE_CountRows; - } - }else - - if( sqliteStrICmp(zLeft, "empty_result_callbacks")==0 ){ - if( getBoolean(zRight) ){ - db->flags |= SQLITE_NullCallback; - }else{ - db->flags &= ~SQLITE_NullCallback; - } - }else - - if( sqliteStrICmp(zLeft, "table_info")==0 ){ - Table *pTab; - Vdbe *v; - pTab = sqliteFindTable(db, zRight); - if( pTab ) v = sqliteGetVdbe(pParse); - if( pTab && v ){ - static VdbeOp tableInfoPreface[] = { - { OP_ColumnName, 0, 0, "cid"}, - { OP_ColumnName, 1, 0, "name"}, - { OP_ColumnName, 2, 0, "type"}, - { OP_ColumnName, 3, 0, "notnull"}, - { OP_ColumnName, 4, 0, "dflt_value"}, - }; - int i; - sqliteVdbeAddOpList(v, ArraySize(tableInfoPreface), tableInfoPreface); - sqliteViewGetColumnNames(pParse, pTab); - for(i=0; i<pTab->nCol; i++){ - sqliteVdbeAddOp(v, OP_Integer, i, 0); - sqliteVdbeAddOp(v, OP_String, 0, 0); - sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zName, P3_STATIC); - sqliteVdbeAddOp(v, OP_String, 0, 0); - sqliteVdbeChangeP3(v, -1, - pTab->aCol[i].zType ? pTab->aCol[i].zType : "numeric", P3_STATIC); - sqliteVdbeAddOp(v, OP_Integer, pTab->aCol[i].notNull, 0); - sqliteVdbeAddOp(v, OP_String, 0, 0); - sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC); - sqliteVdbeAddOp(v, OP_Callback, 5, 0); - } - } - }else - - if( sqliteStrICmp(zLeft, "index_info")==0 ){ - Index *pIdx; - Table *pTab; - Vdbe *v; - pIdx = sqliteFindIndex(db, zRight); - if( pIdx ) v = sqliteGetVdbe(pParse); - if( pIdx && v ){ - static VdbeOp tableInfoPreface[] = { - { OP_ColumnName, 0, 0, "seqno"}, - { OP_ColumnName, 1, 0, "cid"}, - { OP_ColumnName, 2, 0, "name"}, - }; - int i; - pTab = pIdx->pTable; - sqliteVdbeAddOpList(v, ArraySize(tableInfoPreface), tableInfoPreface); - for(i=0; i<pIdx->nColumn; i++){ - int cnum = pIdx->aiColumn[i]; - sqliteVdbeAddOp(v, OP_Integer, i, 0); - sqliteVdbeAddOp(v, OP_Integer, cnum, 0); - sqliteVdbeAddOp(v, OP_String, 0, 0); - assert( pTab->nCol>cnum ); - sqliteVdbeChangeP3(v, -1, pTab->aCol[cnum].zName, P3_STATIC); - sqliteVdbeAddOp(v, OP_Callback, 3, 0); - } - } - }else - - if( sqliteStrICmp(zLeft, "index_list")==0 ){ - Index *pIdx; - Table *pTab; - Vdbe *v; - pTab = sqliteFindTable(db, zRight); - if( pTab ){ - v = sqliteGetVdbe(pParse); - pIdx = pTab->pIndex; - } - if( pTab && pIdx && v ){ - int i = 0; - static VdbeOp indexListPreface[] = { - { OP_ColumnName, 0, 0, "seq"}, - { OP_ColumnName, 1, 0, "name"}, - { OP_ColumnName, 2, 0, "unique"}, - }; - - sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface); - while(pIdx){ - sqliteVdbeAddOp(v, OP_Integer, i, 0); - sqliteVdbeAddOp(v, OP_String, 0, 0); - sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC); - sqliteVdbeAddOp(v, OP_Integer, pIdx->onError!=OE_None, 0); - sqliteVdbeAddOp(v, OP_Callback, 3, 0); - ++i; - pIdx = pIdx->pNext; - } - } - }else - -#ifndef NDEBUG - if( sqliteStrICmp(zLeft, "parser_trace")==0 ){ - extern void sqliteParserTrace(FILE*, char *); - if( getBoolean(zRight) ){ - sqliteParserTrace(stdout, "parser: "); - }else{ - sqliteParserTrace(0, 0); - } - }else -#endif - - if( sqliteStrICmp(zLeft, "integrity_check")==0 ){ - static VdbeOp checkDb[] = { - { OP_SetInsert, 0, 0, "2"}, - { OP_Open, 0, 2, 0}, - { OP_Rewind, 0, 6, 0}, - { OP_Column, 0, 3, 0}, /* 3 */ - { OP_SetInsert, 0, 0, 0}, - { OP_Next, 0, 3, 0}, - { OP_IntegrityCk, 0, 0, 0}, /* 6 */ - { OP_ColumnName, 0, 0, "integrity_check"}, - { OP_Callback, 1, 0, 0}, - { OP_SetInsert, 1, 0, "2"}, - { OP_OpenAux, 1, 2, 0}, - { OP_Rewind, 1, 15, 0}, - { OP_Column, 1, 3, 0}, /* 12 */ - { OP_SetInsert, 1, 0, 0}, - { OP_Next, 1, 12, 0}, - { OP_IntegrityCk, 1, 1, 0}, /* 15 */ - { OP_Callback, 1, 0, 0}, - }; - Vdbe *v = sqliteGetVdbe(pParse); - if( v==0 ) return; - sqliteVdbeAddOpList(v, ArraySize(checkDb), checkDb); - }else - - {} - sqliteFree(zLeft); - sqliteFree(zRight); -} |