?? build.c
字號:
zStmt = sqliteMallocRaw( n ); if( zStmt==0 ) return 0; strcpy(zStmt, p->iDb==1 ? "CREATE TEMP TABLE " : "CREATE TABLE "); k = strlen(zStmt); identPut(zStmt, &k, p->zName); zStmt[k++] = '('; for(i=0; i<p->nCol; i++){ strcpy(&zStmt[k], zSep); k += strlen(&zStmt[k]); zSep = zSep2; identPut(zStmt, &k, p->aCol[i].zName); } strcpy(&zStmt[k], zEnd); return zStmt;}/*** This routine is called to report the final ")" that terminates** a CREATE TABLE statement.**** The table structure that other action routines have been building** is added to the internal hash tables, assuming no errors have** occurred.**** An entry for the table is made in the master table on disk, unless** this is a temporary table or db->init.busy==1. When db->init.busy==1** it means we are reading the sqlite_master table because we just** connected to the database or because the sqlite_master table has** recently changes, so the entry for this table already exists in** the sqlite_master table. We do not want to create it again.**** If the pSelect argument is not NULL, it means that this routine** was called to create a table generated from a ** "CREATE TABLE ... AS SELECT ..." statement. The column names of** the new table will match the result set of the SELECT.*/void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){ Table *p; sqlite *db = pParse->db; if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite_malloc_failed ) return; p = pParse->pNewTable; if( p==0 ) return; /* If the table is generated from a SELECT, then construct the ** list of columns and the text of the table. */ if( pSelect ){ Table *pSelTab = sqliteResultSetOfSelect(pParse, 0, pSelect); if( pSelTab==0 ) return; assert( p->aCol==0 ); p->nCol = pSelTab->nCol; p->aCol = pSelTab->aCol; pSelTab->nCol = 0; pSelTab->aCol = 0; sqliteDeleteTable(0, pSelTab); } /* If the db->init.busy is 1 it means we are reading the SQL off the ** "sqlite_master" or "sqlite_temp_master" table on the disk. ** So do not write to the disk again. Extract the root page number ** for the table from the db->init.newTnum field. (The page number ** should have been put there by the sqliteOpenCb routine.) */ if( db->init.busy ){ p->tnum = db->init.newTnum; } /* If not initializing, then create a record for the new table ** in the SQLITE_MASTER table of the database. The record number ** for the new table entry should already be on the stack. ** ** If this is a TEMPORARY table, write the entry into the auxiliary ** file instead of into the main database file. */ if( !db->init.busy ){ int n; Vdbe *v; v = sqliteGetVdbe(pParse); if( v==0 ) return; if( p->pSelect==0 ){ /* A regular table */ sqliteVdbeOp3(v, OP_CreateTable, 0, p->iDb, (char*)&p->tnum, P3_POINTER); }else{ /* A view */ sqliteVdbeAddOp(v, OP_Integer, 0, 0); } p->tnum = 0; sqliteVdbeAddOp(v, OP_Pull, 1, 0); sqliteVdbeOp3(v, OP_String, 0, 0, p->pSelect==0?"table":"view", P3_STATIC); sqliteVdbeOp3(v, OP_String, 0, 0, p->zName, 0); sqliteVdbeOp3(v, OP_String, 0, 0, p->zName, 0); sqliteVdbeAddOp(v, OP_Dup, 4, 0); sqliteVdbeAddOp(v, OP_String, 0, 0); if( pSelect ){ char *z = createTableStmt(p); n = z ? strlen(z) : 0; sqliteVdbeChangeP3(v, -1, z, n); sqliteFree(z); }else{ assert( pEnd!=0 ); n = Addr(pEnd->z) - Addr(pParse->sFirstToken.z) + 1; sqliteVdbeChangeP3(v, -1, pParse->sFirstToken.z, n); } sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0); sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0); if( !p->iDb ){ sqliteChangeCookie(db, v); } sqliteVdbeAddOp(v, OP_Close, 0, 0); if( pSelect ){ 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); } sqliteEndWriteOperation(pParse); } /* Add the table to the in-memory representation of the database. */ if( pParse->explain==0 && pParse->nErr==0 ){ Table *pOld; FKey *pFKey; 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->aDb[p->iDb].aFKey, pFKey->zTo, nTo); sqliteHashInsert(&db->aDb[p->iDb].aFKey, pFKey->zTo, nTo, pFKey); } pParse->pNewTable = 0; db->nTable++; db->flags |= SQLITE_InternChanges; }}/*** The parser calls this routine in order to create a new VIEW*/void sqliteCreateView( Parse *pParse, /* The parsing context */ Token *pBegin, /* The CREATE token that begins the statement */ Token *pName, /* The token that holds the name of the view */ Select *pSelect, /* A SELECT statement that will become the new view */ int isTemp /* TRUE for a TEMPORARY view */){ Table *p; int n; const char *z; Token sEnd; DbFixer sFix; sqliteStartTable(pParse, pBegin, pName, isTemp, 1); p = pParse->pNewTable; if( p==0 || pParse->nErr ){ sqliteSelectDelete(pSelect); return; } if( sqliteFixInit(&sFix, pParse, p->iDb, "view", pName) && sqliteFixSelect(&sFix, pSelect) ){ sqliteSelectDelete(pSelect); return; } /* Make a copy of the entire SELECT statement that defines the view. ** This will force all the Expr.token.z values to be dynamically ** allocated rather than point to the input string - which means that ** they will persist after the current sqlite_exec() call returns. */ p->pSelect = sqliteSelectDup(pSelect); sqliteSelectDelete(pSelect); if( !pParse->db->init.busy ){ sqliteViewGetColumnNames(pParse, p); } /* Locate the end of the CREATE VIEW statement. Make sEnd point to ** the end. */ sEnd = pParse->sLastToken; if( sEnd.z[0]!=0 && sEnd.z[0]!=';' ){ sEnd.z += sEnd.n; } sEnd.n = 0; n = sEnd.z - pBegin->z; z = pBegin->z; while( n>0 && (z[n-1]==';' || isspace(z[n-1])) ){ n--; } sEnd.z = &z[n-1]; sEnd.n = 1; /* Use sqliteEndTable() to add the view to the SQLITE_MASTER table */ sqliteEndTable(pParse, &sEnd, 0); return;}/*** The Table structure pTable is really a VIEW. Fill in the names of** the columns of the view in the pTable structure. Return the number** of errors. If an error is seen leave an error message in pParse->zErrMsg.*/int sqliteViewGetColumnNames(Parse *pParse, Table *pTable){ ExprList *pEList; Select *pSel; Table *pSelTab; int nErr = 0; assert( pTable ); /* A positive nCol means the columns names for this view are ** already known. */ if( pTable->nCol>0 ) return 0; /* A negative nCol is a special marker meaning that we are currently ** trying to compute the column names. If we enter this routine with ** a negative nCol, it means two or more views form a loop, like this: ** ** CREATE VIEW one AS SELECT * FROM two; ** CREATE VIEW two AS SELECT * FROM one; ** ** Actually, this error is caught previously and so the following test ** should always fail. But we will leave it in place just to be safe. */ if( pTable->nCol<0 ){ sqliteErrorMsg(pParse, "view %s is circularly defined", pTable->zName); return 1; } /* If we get this far, it means we need to compute the table names. */ assert( pTable->pSelect ); /* If nCol==0, then pTable must be a VIEW */ pSel = pTable->pSelect; /* Note that the call to sqliteResultSetOfSelect() will expand any ** "*" elements in this list. But we will need to restore the list ** back to its original configuration afterwards, so we save a copy of ** the original in pEList. */ pEList = pSel->pEList; pSel->pEList = sqliteExprListDup(pEList); if( pSel->pEList==0 ){ pSel->pEList = pEList; return 1; /* Malloc failed */ } pTable->nCol = -1; pSelTab = sqliteResultSetOfSelect(pParse, 0, pSel); if( pSelTab ){ assert( pTable->aCol==0 ); pTable->nCol = pSelTab->nCol; pTable->aCol = pSelTab->aCol; pSelTab->nCol = 0; pSelTab->aCol = 0; sqliteDeleteTable(0, pSelTab); DbSetProperty(pParse->db, pTable->iDb, DB_UnresetViews); }else{ pTable->nCol = 0; nErr++; } sqliteSelectUnbind(pSel); sqliteExprListDelete(pSel->pEList); pSel->pEList = pEList; return nErr; }/*** Clear the column names from the VIEW pTable.**** This routine is called whenever any other table or view is modified.** The view passed into this routine might depend directly or indirectly** on the modified or deleted table so we need to clear the old column** names so that they will be recomputed.*/static void sqliteViewResetColumnNames(Table *pTable){ int i; Column *pCol; assert( pTable!=0 && pTable->pSelect!=0 ); for(i=0, pCol=pTable->aCol; i<pTable->nCol; i++, pCol++){ sqliteFree(pCol->zName); sqliteFree(pCol->zDflt); sqliteFree(pCol->zType); } sqliteFree(pTable->aCol); pTable->aCol = 0; pTable->nCol = 0;}/*** Clear the column names from every VIEW in database idx.*/static void sqliteViewResetAll(sqlite *db, int idx){ HashElem *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); } } DbClearProperty(db, idx, DB_UnresetViews);}/*** Given a token, look up a table with that name. If not found, leave** an error for the parser to find and return NULL.*/Table *sqliteTableFromToken(Parse *pParse, Token *pTok){ char *zName; Table *pTab; zName = sqliteTableNameFromToken(pTok); if( zName==0 ) return 0; pTab = sqliteFindTable(pParse->db, zName, 0); sqliteFree(zName); if( pTab==0 ){ sqliteErrorMsg(pParse, "no such table: %T", pTok); } return pTab;}/*** This routine is called to do the work of a DROP TABLE statement.** pName is the name of the table to be dropped.*/void sqliteDropTable(Parse *pParse, Token *pName, int isView){ Table *pTable; 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 { 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( iDb==1 ){ code = SQLITE_DROP_TEMP_VIEW; }else{ code = SQLITE_DROP_VIEW; } }else{ if( iDb==1 ){ code = SQLITE_DROP_TEMP_TABLE; }else{ code = SQLITE_DROP_TABLE; } } if( sqliteAuthCheck(pParse, code, pTable->zName, 0, zDb) ){ return; } if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTable->zName, 0, zDb) ){ return; } }#endif if( pTable->readOnly ){ sqliteErrorMsg(pParse, "table %s may not be dropped", pTable->zName); pParse->nErr++; return; } if( isView && pTable->pSelect==0 ){ sqliteErrorMsg(pParse, "use DROP TABLE to delete table %s", pTable->zName); return; } if( !isView && pTable->pSelect ){ sqliteErrorMsg(pParse, "use DROP VIEW to delete view %s", pTable->zName); return; } /* Generate code to remove the table from the master table ** on disk. */ v = sqliteGetVdbe(pParse); if( v ){ static VdbeOpList dropTable[] = { { OP_Rewind, 0, ADDR(8), 0}, { OP_String, 0, 0, 0}, /* 1 */ { OP_MemStore, 1, 1, 0}, { OP_MemLoad, 1, 0, 0}, /* 3 */ { OP_Column, 0, 2, 0}, { OP_Ne, 0, ADDR(7), 0}, { OP_Delete, 0, 0, 0}, { OP_Next, 0, ADDR(3), 0}, /* 7 */ }; Index *pIdx; Trigger *pTrigger; sqliteBeginWriteOperation(pParse, 0, pTable->iDb); /* Drop all triggers associated with the table being dropped */ pTrigger = pTable->pTrigger; while( pTrigger ){ assert( pTrigger->iDb==pTable->iDb || pTrigger->iDb==1 ); sqliteDropTriggerPtr(pParse, pTrigger, 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); /* Drop all SQLITE_TEMP_MASTER entries that refer to the table */ if( pTable->iDb!=1 ){ sqliteOpenMasterTable(v, 1); base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -