📄 build.c
字号:
/*** 2001 September 15**** The author disclaims copyright to this source code. In place of** a legal notice, here is a blessing:**** May you do good and not evil.** May you find forgiveness for yourself and forgive others.** May you share freely, never taking more than you give.***************************************************************************** This file contains C code routines that are called by the SQLite parser** when syntax rules are reduced. The routines in this file handle the** following kinds of SQL syntax:**** CREATE TABLE** DROP TABLE** CREATE INDEX** DROP INDEX** creating ID lists** BEGIN TRANSACTION** COMMIT** ROLLBACK** PRAGMA**** $Id: build.c,v 1.157 2003/07/30 12:34:12 drh Exp $*/#include "sqliteInt.h"#include <ctype.h>/*** This routine is called when a new SQL statement is beginning to** be parsed. Check to see if the schema for the database needs** to be read from the SQLITE_MASTER and SQLITE_TEMP_MASTER tables.** If it does, then read it.*/void sqliteBeginParse(Parse *pParse, int explainFlag){ sqlite *db = pParse->db; int i; pParse->explain = explainFlag; if((db->flags & SQLITE_Initialized)==0 && pParse->initFlag==0 ){ int rc = sqliteInit(db, &pParse->zErrMsg); if( rc!=SQLITE_OK ){ pParse->rc = rc; pParse->nErr++; } } for(i=0; i<db->nDb; i++){ DbClearProperty(db, i, DB_Locked); if( !db->aDb[i].inTrans ){ DbClearProperty(db, i, DB_Cookie); } }}/*** This is a fake callback procedure used when sqlite_exec() is** invoked with a NULL callback pointer. If we pass a NULL callback** pointer into sqliteVdbeExec() it will return at every OP_Callback,** which we do not want it to do. So we substitute a pointer to this** procedure in place of the NULL.*/static int fakeCallback(void *NotUsed, int n, char **az1, char **az2){ return 0;}/*** This routine is called after a single SQL statement has been** parsed and we want to execute the VDBE code to implement ** that statement. Prior action routines should have already** constructed VDBE code to do the work of the SQL statement.** This routine just has to execute the VDBE code.**** Note that if an error occurred, it might be the case that** no VDBE code was generated.*/void sqliteExec(Parse *pParse){ int rc = SQLITE_OK; sqlite *db = pParse->db; Vdbe *v = pParse->pVdbe; int (*xCallback)(void*,int,char**,char**); if( sqlite_malloc_failed ) return; xCallback = pParse->xCallback; if( xCallback==0 && pParse->useCallback ) xCallback = fakeCallback; if( v && pParse->nErr==0 ){ FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0; sqliteVdbeTrace(v, trace); sqliteVdbeMakeReady(v, xCallback, pParse->pArg, pParse->explain); if( pParse->useCallback ){ if( pParse->explain ){ rc = sqliteVdbeList(v); db->next_cookie = db->aDb[0].schema_cookie; }else{ sqliteVdbeExec(v); } rc = sqliteVdbeFinalize(v, &pParse->zErrMsg); if( rc ) pParse->nErr++; pParse->pVdbe = 0; pParse->rc = rc; if( rc ) pParse->nErr++; }else{ pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE; } pParse->colNamesSet = 0; }else if( pParse->useCallback==0 ){ pParse->rc = SQLITE_ERROR; } pParse->nTab = 0; pParse->nMem = 0; pParse->nSet = 0; pParse->nAgg = 0;}/*** 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 zDatabase is 0, all databases are searched for the** table and the first matching table is returned. (No checking** for duplicate table names is done.) The search order is** TEMP first, then MAIN, then any auxiliary databases added** using the ATTACH command.**** See also sqliteLocateTable().*/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.** Also leave an error message in pParse->zErrMsg.**** The difference between this routine and sqliteFindTable()** is that this routine leaves an error message in pParse->zErrMsg** where sqliteFindTable() does not.*/Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){ Table *p; p = sqliteFindTable(pParse->db, zName, zDbase); if( p==0 ){ if( zDbase ){ sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName); }else if( sqliteFindTable(pParse->db, zName, 0)!=0 ){ sqliteErrorMsg(pParse, "table \"%s\" is not in database \"%s\"", zName, zDbase); }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** and the name of the database that contains the index.** Return NULL if not found.**** If zDatabase is 0, all databases are searched for the** table and the first matching index is returned. (No checking** for duplicate index names is done.) The search order is** TEMP first, then MAIN, then any auxiliary databases added** using the ATTACH command.*/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;}/*** Remove the given index from the index hash table, and free** its memory structures.**** The index is removed from the database hash tables but** it is not unlinked from the Table that it indexes.** Unlinking from the Table must be done by the calling function.*/static void sqliteDeleteIndex(sqlite *db, Index *p){ Index *pOld; assert( db!=0 && p->zName!=0 ); pOld = sqliteHashInsert(&db->aDb[p->iDb].idxHash, p->zName, strlen(p->zName)+1, 0); if( pOld!=0 && pOld!=p ){ sqliteHashInsert(&db->aDb[p->iDb].idxHash, pOld->zName, strlen(pOld->zName)+1, pOld); } sqliteFree(p);}/*** Unlink the given index from its table, then remove** the index from the index hash table and free its memory** structures.*/void sqliteUnlinkAndDeleteIndex(sqlite *db, Index *pIndex){ if( pIndex->pTable->pIndex==pIndex ){ pIndex->pTable->pIndex = pIndex->pNext; }else{ Index *p; for(p=pIndex->pTable->pIndex; p && p->pNext!=pIndex; p=p->pNext){} if( p && p->pNext==pIndex ){ p->pNext = pIndex->pNext; } } sqliteDeleteIndex(db, pIndex);}/*** Erase all schema information from the in-memory hash tables of** database connection. This routine is called to reclaim memory** before the connection closes. It is also called during a rollback** if there were schema changes during the transaction.**** 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, 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; /* 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; }}/*** This routine is called whenever a rollback occurs. If there were** schema changes during the transaction, then we have to reset the** internal hash tables and reload them from disk.*/void sqliteRollbackInternalChanges(sqlite *db){ if( db->flags & SQLITE_InternChanges ){ sqliteResetInternalSchema(db, 0); }}/*** This routine is called when a commit occurs.*/void sqliteCommitInternalChanges(sqlite *db){ db->aDb[0].schema_cookie = db->next_cookie; db->flags &= ~SQLITE_InternChanges;}/*** Remove the memory data structures associated with the given** Table. No changes are made to disk by this routine.**** This routine just deletes the data structure. It does not unlink** the table data structure from the hash table. Nor does it remove** foreign keys from the sqlite.aFKey hash table. But it does destroy** memory structures of the indices and foreign keys associated with ** the table.**** Indices associated with the table are unlinked from the "db"** data structure if db!=NULL. If db==NULL, indices attached to** the table are deleted, but it is assumed they have already been** unlinked.*/void sqliteDeleteTable(sqlite *db, Table *pTable){ int i; Index *pIndex, *pNext; FKey *pFKey, *pNextFKey; if( pTable==0 ) return; /* Delete all indices associated with this table */ for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ pNext = pIndex->pNext; assert( pIndex->iDb==pTable->iDb || (pTable->iDb==0 && pIndex->iDb==1) ); sqliteDeleteIndex(db, pIndex); } /* Delete all foreign keys associated with this table. The keys ** should have already been unlinked from the db->aFKey hash table */ for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){ pNextFKey = pFKey->pNextFrom; assert( pTable->iDb<db->nDb ); assert( sqliteHashFind(&db->aDb[pTable->iDb].aFKey, pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey ); sqliteFree(pFKey); } /* Delete the Table structure itself. */ for(i=0; i<pTable->nCol; i++){ sqliteFree(pTable->aCol[i].zName); sqliteFree(pTable->aCol[i].zDflt); sqliteFree(pTable->aCol[i].zType); } sqliteFree(pTable->zName); sqliteFree(pTable->aCol); sqliteSelectDelete(pTable->pSelect); sqliteFree(pTable);}/*** Unlink the given table from the hash tables and the delete the** table structure with all its indices and foreign keys.*/static void sqliteUnlinkAndDeleteTable(sqlite *db, Table *p){ Table *pOld; FKey *pF1, *pF2; int i = p->iDb; assert( db!=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->aDb[i].aFKey, pF1->zTo, nTo); if( pF2==pF1 ){ sqliteHashInsert(&db->aDb[i].aFKey, pF1->zTo, nTo, pF1->pNextTo); }else{ while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; } if( pF2 ){ pF2->pNextTo = pF1->pNextTo; } } } sqliteDeleteTable(db, p);}/*** Construct the name of a user table or index from a token.**** Space to hold the name is obtained from sqliteMalloc() and must** be freed by the calling function.*/char *sqliteTableNameFromToken(Token *pName){ char *zName = sqliteStrNDup(pName->z, pName->n); sqliteDequote(zName); return zName;}/*** Generate code to open the appropriate master table. The table** opened will be SQLITE_MASTER for persistent tables and ** SQLITE_TEMP_MASTER for temporary tables. The table is opened** on cursor 0.*/void sqliteOpenMasterTable(Vdbe *v, int isTemp){ sqliteVdbeAddOp(v, OP_Integer, isTemp, 0); sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2);}/*** Begin constructing a new table representation in memory. This is
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -