/* ** Add a new local variable to the block currently being constructed. ** ** The parser calls this routine once for each variable declaration ** in a DECLARE ... BEGIN statement. sqliteStartBlock() gets called ** first to get things going. Then this routine is called for each ** variable. */ void sqliteAddProcVar(Parse *pParse, Token *pName){ Block *p; int i; char *z = 0; Variable *pVar; if( (p = pParse->pCurrentBlock)==0 ) return; sqliteSetNString(&z, pName->z, pName->n, 0); if( z==0 ) return; sqliteDequote(z); for(i=0; i<p->nVar; i++){ if( strcmp(z, p->aVar[i].zName)==0 ){ sqliteErrorMsg(pParse, "duplicate variable name: %s", z); sqliteFree(z); return; } } if( (p->nVar & 0x7)==0 ){ Variable *aNew; aNew = sqliteRealloc( p->aVar, (p->nVar+8)*sizeof(p->aVar[0])); if( aNew==0 ) return; p->aVar = aNew; } pVar = &p->aVar[p->nVar]; memset(pVar, 0, sizeof(p->aVar[0])); pVar->zName = z; pVar->mVar = pParse->nMem++; pVar->isParam = p->params; p->nVar++; }
/* ** Add a new element to the end of an expression list. If pList is ** initially NULL, then create a new expression list. */ ExprList *sqliteExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){ if( pList==0 ){ pList = sqliteMalloc( sizeof(ExprList) ); if( pList==0 ){ /* sqliteExprDelete(pExpr); // Leak memory if malloc fails */ return 0; } assert( pList->nAlloc==0 ); } if( pList->nAlloc<=pList->nExpr ){ pList->nAlloc = pList->nAlloc*2 + 4; pList->a = sqliteRealloc(pList->a, pList->nAlloc*sizeof(pList->a[0])); if( pList->a==0 ){ /* sqliteExprDelete(pExpr); // Leak memory if malloc fails */ pList->nExpr = pList->nAlloc = 0; return pList; } } assert( pList->a!=0 ); if( pExpr || pName ){ struct ExprList_item *pItem = &pList->a[pList->nExpr++]; memset(pItem, 0, sizeof(*pItem)); pItem->pExpr = pExpr; if( pName ){ sqliteSetNString(&pItem->zName, pName->z, pName->n, 0); sqliteDequote(pItem->zName); } } return pList; }
/* ** This routine is called by the parser while in the middle of ** parsing a variable declaration in a procedure block. The pFirst ** token is the first token in the sequence of tokens that describe ** the type of the variable currently under construction. pLast is ** the last token in the sequence. Use this information to ** construct a string that contains the typename of the variable ** and store that string in zType. */ void sqliteAddProcVarType(Parse *pParse, Token *pFirst, Token *pLast){ Block *p; int i, j; int n; char *z, **pz; Variable *pVar; if( (p = pParse->pCurrentBlock)==0 ) return; i = p->nVar-1; if( i<0 ) return; pVar = &p->aVar[i]; pz = &pVar->zType; n = pLast->n + Addr(pLast->z) - Addr(pFirst->z); sqliteSetNString(pz, pFirst->z, n, 0); z = *pz; if( z==0 ) return; for(i=j=0; z[i]; i++){ int c = z[i]; if( isspace(c) ) continue; z[j++] = c; } z[j] = 0; }
/* ** Change the value of the P3 operand for a specific instruction. ** This routine is useful when a large program is loaded from a ** static array using sqliteVdbeAddOpList but we want to make a ** few minor changes to the program. ** ** If n>=0 then the P3 operand is dynamic, meaning that a copy of ** the string is made into memory obtained from sqliteMalloc(). ** A value of n==0 means copy bytes of zP3 up to and including the ** first null byte. If n>0 then copy n+1 bytes of zP3. ** ** If n==P3_STATIC it means that zP3 is a pointer to a constant static ** string and we can just copy the pointer. n==P3_POINTER means zP3 is ** a pointer to some object other than a string. ** ** If addr<0 then change P3 on the most recently inserted instruction. */ void sqliteVdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){ Op *pOp; assert( p->magic==VDBE_MAGIC_INIT ); if( p==0 || p->aOp==0 ) return; if( addr<0 || addr>=p->nOp ){ addr = p->nOp - 1; if( addr<0 ) return; } pOp = &p->aOp[addr]; if( pOp->p3 && pOp->p3type==P3_DYNAMIC ){ sqliteFree(pOp->p3); pOp->p3 = 0; } if( zP3==0 ){ pOp->p3 = 0; pOp->p3type = P3_NOTUSED; }else if( n<0 ){ pOp->p3 = (char*)zP3; pOp->p3type = n; }else{ sqliteSetNString(&pOp->p3, zP3, n, 0); pOp->p3type = P3_DYNAMIC; } }
/* ** This routine is called by the parser to process an ATTACH statement: ** ** ATTACH DATABASE filename AS dbname ** ** The pFilename and pDbname arguments are the tokens that define the ** filename and dbname in the ATTACH statement. */ void sqliteAttach(Parse *pParse, Token *pFilename, Token *pDbname, Token *pKey){ Db *aNew; int rc, i; char *zFile, *zName; sqlite *db; Vdbe *v; v = sqliteGetVdbe(pParse); sqliteVdbeAddOp(v, OP_Halt, 0, 0); if( pParse->explain ) return; db = pParse->db; if( db->file_format<4 ){ sqliteErrorMsg(pParse, "cannot attach auxiliary databases to an " "older format master database", 0); pParse->rc = SQLITE_ERROR; return; } if( db->nDb>=MAX_ATTACHED+2 ){ sqliteErrorMsg(pParse, "too many attached databases - max %d", MAX_ATTACHED); pParse->rc = SQLITE_ERROR; return; } zFile = 0; sqliteSetNString(&zFile, pFilename->z, pFilename->n, 0); if( zFile==0 ) return; sqliteDequote(zFile); #ifndef SQLITE_OMIT_AUTHORIZATION if( sqliteAuthCheck(pParse, SQLITE_ATTACH, zFile, 0, 0)!=SQLITE_OK ){ sqliteFree(zFile); return; } #endif /* SQLITE_OMIT_AUTHORIZATION */ zName = 0; sqliteSetNString(&zName, pDbname->z, pDbname->n, 0); if( zName==0 ) return; sqliteDequote(zName); for(i=0; i<db->nDb; i++){ if( db->aDb[i].zName && sqliteStrICmp(db->aDb[i].zName, zName)==0 ){ sqliteErrorMsg(pParse, "database %z is already in use", zName); pParse->rc = SQLITE_ERROR; sqliteFree(zFile); return; } } if( db->aDb==db->aDbStatic ){ aNew = sqliteMalloc( sizeof(db->aDb[0])*3 ); if( aNew==0 ) return; memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); }else{ aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); if( aNew==0 ) return; } db->aDb = aNew; aNew = &db->aDb[db->nDb++]; memset(aNew, 0, sizeof(*aNew)); sqliteHashInit(&aNew->tblHash, SQLITE_HASH_STRING, 0); sqliteHashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0); sqliteHashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0); sqliteHashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1); aNew->zName = zName; rc = sqliteBtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt); if( rc ){ sqliteErrorMsg(pParse, "unable to open database: %s", zFile); } #if SQLITE_HAS_CODEC { extern int sqliteCodecAttach(sqlite*, int, void*, int); char *zKey = 0; int nKey; if( pKey && pKey->z && pKey->n ){ sqliteSetNString(&zKey, pKey->z, pKey->n, 0); sqliteDequote(zKey); nKey = strlen(zKey); }else{ zKey = 0; nKey = 0; } sqliteCodecAttach(db, db->nDb-1, zKey, nKey); } #endif sqliteFree(zFile); db->flags &= ~SQLITE_Initialized; if( pParse->nErr ) return; if( rc==SQLITE_OK ){ rc = sqliteInit(pParse->db, &pParse->zErrMsg); } if( rc ){ int i = db->nDb - 1; assert( i>=2 ); if( db->aDb[i].pBt ){ sqliteBtreeClose(db->aDb[i].pBt); db->aDb[i].pBt = 0; } sqliteResetInternalSchema(db, 0); pParse->nErr++; pParse->rc = SQLITE_ERROR; } }
/* ** 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; Vdbe *v = sqliteGetVdbe(pParse); if( v==0 ) return; 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, 0) ){ 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 VdbeOpList 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, 0, 0, 0}, /* 5 */ { OP_ColumnName, 0, 1, "cache_size"}, { OP_Callback, 1, 0, 0}, }; int addr; if( pRight->z==pLeft->z ){ addr = sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); sqliteVdbeChangeP1(v, addr+5, MAX_PAGES); }else{ 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->aDb[0].pBt, 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 VdbeOpList getCacheSize[] = { { OP_ColumnName, 0, 1, "cache_size"}, { OP_Callback, 1, 0, 0}, }; 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->aDb[0].pBt, 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 VdbeOpList getSync[] = { { OP_ColumnName, 0, 1, "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} }; 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->aDb[0].pBt, db->cache_size); sqliteBtreeSetSafetyLevel(db->aDb[0].pBt, 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 VdbeOpList getSync[] = { { OP_ColumnName, 0, 1, "synchronous"}, { OP_Callback, 1, 0, 0}, }; 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->aDb[0].pBt, db->cache_size); sqliteBtreeSetSafetyLevel(db->aDb[0].pBt, db->safety_level); } }else #ifndef NDEBUG if( sqliteStrICmp(zLeft, "trigger_overhead_test")==0 ){ if( getBoolean(zRight) ){ always_code_trigger_setup = 1; }else{ always_code_trigger_setup = 0; } }else #endif if( flagPragma(pParse, zLeft, zRight) ){ /* The flagPragma() call also generates any necessary code */ }else if( sqliteStrICmp(zLeft, "table_info")==0 ){ Table *pTab; pTab = sqliteFindTable(db, zRight, 0); if( pTab ){ static VdbeOpList 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"}, { OP_ColumnName, 5, 1, "pk"}, }; int i; sqliteVdbeAddOpList(v, ArraySize(tableInfoPreface), tableInfoPreface); sqliteViewGetColumnNames(pParse, pTab); for(i=0; i<pTab->nCol; i++){ sqliteVdbeAddOp(v, OP_Integer, i, 0); sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zName, 0); sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zType ? pTab->aCol[i].zType : "numeric", 0); sqliteVdbeAddOp(v, OP_Integer, pTab->aCol[i].notNull, 0); sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zDflt, P3_STATIC); sqliteVdbeAddOp(v, OP_Integer, pTab->aCol[i].isPrimKey, 0); sqliteVdbeAddOp(v, OP_Callback, 6, 0); } } }else if( sqliteStrICmp(zLeft, "index_info")==0 ){ Index *pIdx; Table *pTab; pIdx = sqliteFindIndex(db, zRight, 0); if( pIdx ){ static VdbeOpList tableInfoPreface[] = { { OP_ColumnName, 0, 0, "seqno"}, { OP_ColumnName, 1, 0, "cid"}, { OP_ColumnName, 2, 1, "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); assert( pTab->nCol>cnum ); sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[cnum].zName, 0); sqliteVdbeAddOp(v, OP_Callback, 3, 0); } } }else if( sqliteStrICmp(zLeft, "index_list")==0 ){ Index *pIdx; Table *pTab; pTab = sqliteFindTable(db, zRight, 0); if( pTab ){ v = sqliteGetVdbe(pParse); pIdx = pTab->pIndex; } if( pTab && pIdx ){ int i = 0; static VdbeOpList indexListPreface[] = { { OP_ColumnName, 0, 0, "seq"}, { OP_ColumnName, 1, 0, "name"}, { OP_ColumnName, 2, 1, "unique"}, }; sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface); while(pIdx){ sqliteVdbeAddOp(v, OP_Integer, i, 0); sqliteVdbeOp3(v, OP_String, 0, 0, pIdx->zName, 0); sqliteVdbeAddOp(v, OP_Integer, pIdx->onError!=OE_None, 0); sqliteVdbeAddOp(v, OP_Callback, 3, 0); ++i; pIdx = pIdx->pNext; } } }else if( sqliteStrICmp(zLeft, "foreign_key_list")==0 ){ FKey *pFK; Table *pTab; pTab = sqliteFindTable(db, zRight, 0); if( pTab ){ v = sqliteGetVdbe(pParse); pFK = pTab->pFKey; } if( pTab && pFK ){ int i = 0; static VdbeOpList indexListPreface[] = { { OP_ColumnName, 0, 0, "id"}, { OP_ColumnName, 1, 0, "seq"}, { OP_ColumnName, 2, 0, "table"}, { OP_ColumnName, 3, 0, "from"}, { OP_ColumnName, 4, 1, "to"}, }; sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface); while(pFK){ int j; for(j=0; j<pFK->nCol; j++){ sqliteVdbeAddOp(v, OP_Integer, i, 0); sqliteVdbeAddOp(v, OP_Integer, j, 0); sqliteVdbeOp3(v, OP_String, 0, 0, pFK->zTo, 0); sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[pFK->aCol[j].iFrom].zName, 0); sqliteVdbeOp3(v, OP_String, 0, 0, pFK->aCol[j].zCol, 0); sqliteVdbeAddOp(v, OP_Callback, 5, 0); } ++i; pFK = pFK->pNextFrom; } } }else if( sqliteStrICmp(zLeft, "database_list")==0 ){ int i; static VdbeOpList indexListPreface[] = { { OP_ColumnName, 0, 0, "seq"}, { OP_ColumnName, 1, 0, "name"}, { OP_ColumnName, 2, 1, "file"}, }; sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface); for(i=0; i<db->nDb; i++){ if( db->aDb[i].pBt==0 ) continue; assert( db->aDb[i].zName!=0 ); sqliteVdbeAddOp(v, OP_Integer, i, 0); sqliteVdbeOp3(v, OP_String, 0, 0, db->aDb[i].zName, 0); sqliteVdbeOp3(v, OP_String, 0, 0, sqliteBtreeGetFilename(db->aDb[i].pBt), 0); sqliteVdbeAddOp(v, OP_Callback, 3, 0); } }else /* ** PRAGMA temp_store ** PRAGMA temp_store = "default"|"memory"|"file" ** ** Return or set the local value of the temp_store 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. ** ** Note that it is possible for the library compile-time options to ** override this setting */ if( sqliteStrICmp(zLeft, "temp_store")==0 ){ static VdbeOpList getTmpDbLoc[] = { { OP_ColumnName, 0, 1, "temp_store"}, { OP_Callback, 1, 0, 0}, }; if( pRight->z==pLeft->z ){ sqliteVdbeAddOp(v, OP_Integer, db->temp_store, 0); sqliteVdbeAddOpList(v, ArraySize(getTmpDbLoc), getTmpDbLoc); }else{ changeTempStorage(pParse, zRight); } }else /* ** PRAGMA default_temp_store ** PRAGMA default_temp_store = "default"|"memory"|"file" ** ** Return or set the value of the persistent temp_store flag. Any ** change does not take effect until the next time the database is ** opened. ** ** Note that it is possible for the library compile-time options to ** override this setting */ if( sqliteStrICmp(zLeft, "default_temp_store")==0 ){ static VdbeOpList getTmpDbLoc[] = { { OP_ColumnName, 0, 1, "temp_store"}, { OP_ReadCookie, 0, 5, 0}, { OP_Callback, 1, 0, 0}}; if( pRight->z==pLeft->z ){ sqliteVdbeAddOpList(v, ArraySize(getTmpDbLoc), getTmpDbLoc); }else{ sqliteBeginWriteOperation(pParse, 0, 0); sqliteVdbeAddOp(v, OP_Integer, getTempStore(zRight), 0); sqliteVdbeAddOp(v, OP_SetCookie, 0, 5); sqliteEndWriteOperation(pParse); } }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 ){ int i, j, addr; /* Code that initializes the integrity check program. Set the ** error count 0 */ static VdbeOpList initCode[] = { { OP_Integer, 0, 0, 0}, { OP_MemStore, 0, 1, 0}, { OP_ColumnName, 0, 1, "integrity_check"}, }; /* Code to do an BTree integrity check on a single database file. */ static VdbeOpList checkDb[] = { { OP_SetInsert, 0, 0, "2"}, { OP_Integer, 0, 0, 0}, /* 1 */ { OP_OpenRead, 0, 2, 0}, { OP_Rewind, 0, 7, 0}, /* 3 */ { OP_Column, 0, 3, 0}, /* 4 */ { OP_SetInsert, 0, 0, 0}, { OP_Next, 0, 4, 0}, /* 6 */ { OP_IntegrityCk, 0, 0, 0}, /* 7 */ { OP_Dup, 0, 1, 0}, { OP_String, 0, 0, "ok"}, { OP_StrEq, 0, 12, 0}, /* 10 */ { OP_MemIncr, 0, 0, 0}, { OP_String, 0, 0, "*** in database "}, { OP_String, 0, 0, 0}, /* 13 */ { OP_String, 0, 0, " ***\n"}, { OP_Pull, 3, 0, 0}, { OP_Concat, 4, 1, 0}, { OP_Callback, 1, 0, 0}, }; /* Code that appears at the end of the integrity check. If no error ** messages have been generated, output OK. Otherwise output the ** error message */ static VdbeOpList endCode[] = { { OP_MemLoad, 0, 0, 0}, { OP_Integer, 0, 0, 0}, { OP_Ne, 0, 0, 0}, /* 2 */ { OP_String, 0, 0, "ok"}, { OP_Callback, 1, 0, 0}, }; /* Initialize the VDBE program */ sqliteVdbeAddOpList(v, ArraySize(initCode), initCode); /* Do an integrity check on each database file */ for(i=0; i<db->nDb; i++){ HashElem *x; /* Do an integrity check of the B-Tree */ addr = sqliteVdbeAddOpList(v, ArraySize(checkDb), checkDb); sqliteVdbeChangeP1(v, addr+1, i); sqliteVdbeChangeP2(v, addr+3, addr+7); sqliteVdbeChangeP2(v, addr+6, addr+4); sqliteVdbeChangeP2(v, addr+7, i); sqliteVdbeChangeP2(v, addr+10, addr+ArraySize(checkDb)); sqliteVdbeChangeP3(v, addr+13, db->aDb[i].zName, P3_STATIC); /* Make sure all the indices are constructed correctly. */ sqliteCodeVerifySchema(pParse, i); for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); Index *pIdx; int loopTop; if( pTab->pIndex==0 ) continue; sqliteVdbeAddOp(v, OP_Integer, i, 0); sqliteVdbeOp3(v, OP_OpenRead, 1, pTab->tnum, pTab->zName, 0); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ if( pIdx->tnum==0 ) continue; sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); sqliteVdbeOp3(v, OP_OpenRead, j+2, pIdx->tnum, pIdx->zName, 0); } sqliteVdbeAddOp(v, OP_Integer, 0, 0); sqliteVdbeAddOp(v, OP_MemStore, 1, 1); loopTop = sqliteVdbeAddOp(v, OP_Rewind, 1, 0); sqliteVdbeAddOp(v, OP_MemIncr, 1, 0); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int k, jmp2; static VdbeOpList idxErr[] = { { OP_MemIncr, 0, 0, 0}, { OP_String, 0, 0, "rowid "}, { OP_Recno, 1, 0, 0}, { OP_String, 0, 0, " missing from index "}, { OP_String, 0, 0, 0}, /* 4 */ { OP_Concat, 4, 0, 0}, { OP_Callback, 1, 0, 0}, }; sqliteVdbeAddOp(v, OP_Recno, 1, 0); for(k=0; k<pIdx->nColumn; k++){ int idx = pIdx->aiColumn[k]; if( idx==pTab->iPKey ){ sqliteVdbeAddOp(v, OP_Recno, 1, 0); }else{ sqliteVdbeAddOp(v, OP_Column, 1, idx); } } sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0); if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx); jmp2 = sqliteVdbeAddOp(v, OP_Found, j+2, 0); addr = sqliteVdbeAddOpList(v, ArraySize(idxErr), idxErr); sqliteVdbeChangeP3(v, addr+4, pIdx->zName, P3_STATIC); sqliteVdbeChangeP2(v, jmp2, sqliteVdbeCurrentAddr(v)); } sqliteVdbeAddOp(v, OP_Next, 1, loopTop+1); sqliteVdbeChangeP2(v, loopTop, sqliteVdbeCurrentAddr(v)); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ static VdbeOpList cntIdx[] = { { OP_Integer, 0, 0, 0}, { OP_MemStore, 2, 1, 0}, { OP_Rewind, 0, 0, 0}, /* 2 */ { OP_MemIncr, 2, 0, 0}, { OP_Next, 0, 0, 0}, /* 4 */ { OP_MemLoad, 1, 0, 0}, { OP_MemLoad, 2, 0, 0}, { OP_Eq, 0, 0, 0}, /* 7 */ { OP_MemIncr, 0, 0, 0}, { OP_String, 0, 0, "wrong # of entries in index "}, { OP_String, 0, 0, 0}, /* 10 */ { OP_Concat, 2, 0, 0}, { OP_Callback, 1, 0, 0}, }; if( pIdx->tnum==0 ) continue; addr = sqliteVdbeAddOpList(v, ArraySize(cntIdx), cntIdx); sqliteVdbeChangeP1(v, addr+2, j+2); sqliteVdbeChangeP2(v, addr+2, addr+5); sqliteVdbeChangeP1(v, addr+4, j+2); sqliteVdbeChangeP2(v, addr+4, addr+3); sqliteVdbeChangeP2(v, addr+7, addr+ArraySize(cntIdx)); sqliteVdbeChangeP3(v, addr+10, pIdx->zName, P3_STATIC); } } } addr = sqliteVdbeAddOpList(v, ArraySize(endCode), endCode); sqliteVdbeChangeP2(v, addr+2, addr+ArraySize(endCode)); }else {} sqliteFree(zLeft); sqliteFree(zRight); }
/* ** Run the parser on the given SQL string. The parser structure is ** passed in. An SQLITE_ status code is returned. If an error occurs ** and pzErrMsg!=NULL then an error message might be written into ** memory obtained from malloc() and *pzErrMsg made to point to that ** error message. Or maybe not. */ int sqliteRunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ int nErr = 0; int i; void *pEngine; int tokenType; int lastTokenParsed = -1; sqlite *db = pParse->db; extern void *sqliteParserAlloc(void*(*)(int)); extern void sqliteParserFree(void*, void(*)(void*)); extern int sqliteParser(void*, int, Token, Parse*); db->flags &= ~SQLITE_Interrupt; pParse->rc = SQLITE_OK; i = 0; pEngine = sqliteParserAlloc((void*(*)(int))malloc); if( pEngine==0 ){ sqliteSetString(pzErrMsg, "out of memory", (char*)0); return 1; } pParse->sLastToken.dyn = 0; pParse->zTail = zSql; while( sqlite_malloc_failed==0 && zSql[i]!=0 ){ assert( i>=0 ); pParse->sLastToken.z = &zSql[i]; assert( pParse->sLastToken.dyn==0 ); pParse->sLastToken.n = sqliteGetToken((unsigned char*)&zSql[i], &tokenType); i += pParse->sLastToken.n; switch( tokenType ){ case TK_SPACE: case TK_COMMENT: { if( (db->flags & SQLITE_Interrupt)!=0 ){ pParse->rc = SQLITE_INTERRUPT; sqliteSetString(pzErrMsg, "interrupt", (char*)0); goto abort_parse; } break; } case TK_ILLEGAL: { sqliteSetNString(pzErrMsg, "unrecognized token: \"", -1, pParse->sLastToken.z, pParse->sLastToken.n, "\"", 1, 0); nErr++; goto abort_parse; } case TK_SEMI: { pParse->zTail = &zSql[i]; /* Fall thru into the default case */ } default: { sqliteParser(pEngine, tokenType, pParse->sLastToken, pParse); lastTokenParsed = tokenType; if( pParse->rc!=SQLITE_OK ){ goto abort_parse; } break; } } } abort_parse: if( zSql[i]==0 && nErr==0 && pParse->rc==SQLITE_OK ){ if( lastTokenParsed!=TK_SEMI ){ sqliteParser(pEngine, TK_SEMI, pParse->sLastToken, pParse); pParse->zTail = &zSql[i]; } sqliteParser(pEngine, 0, pParse->sLastToken, pParse); } sqliteParserFree(pEngine, free); if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){ sqliteSetString(&pParse->zErrMsg, sqlite_error_string(pParse->rc), (char*)0); } if( pParse->zErrMsg ){ if( pzErrMsg && *pzErrMsg==0 ){ *pzErrMsg = pParse->zErrMsg; }else{ sqliteFree(pParse->zErrMsg); } pParse->zErrMsg = 0; if( !nErr ) nErr++; } if( pParse->pVdbe && pParse->nErr>0 ){ sqliteVdbeDelete(pParse->pVdbe); pParse->pVdbe = 0; } if( pParse->pNewTable ){ sqliteDeleteTable(pParse->db, pParse->pNewTable); pParse->pNewTable = 0; } if( pParse->pNewTrigger ){ sqliteDeleteTrigger(pParse->pNewTrigger); pParse->pNewTrigger = 0; } if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){ pParse->rc = SQLITE_ERROR; } return nErr; }
static int sqliteCompileStmt( Parse *pParse, /* parse context */ Block *b, /* current block */ Stmt* pStmt, /* statement to compile */ int *tailgoto, /* set *tailgoto to 1 if last statement is a goto */ int in_excep /* set to 1 when compiling an exception handler */ ){ Vdbe *v = sqliteGetVdbe(pParse); SrcList dummy; int i, j, skipgoto = 0; dummy.nSrc = 0; if( tailgoto ) *tailgoto = 0; if( pStmt->op!=TK_RAISE && pStmt->op!=TK_PROCEDURE && pStmt->pExpr1 ){ Expr *pExpr = pStmt->pExpr1; if( pStmt->op==TK_FOR ) { /* allocate the FOR counter variable (see case TK_FOR below) */ sqliteAddProcVar(pParse, &(pExpr->pLeft->token)); } if( sqliteExprProcResolve(pParse, b, pExpr) ){ return 1; } if( sqliteExprCheck(pParse, pExpr, 0, 0) ){ return 1; } } switch( pStmt->op ) { case TK_ASSIGN:{ Expr *pLeft = pStmt->pExpr1->pLeft; Expr *pRight = pStmt->pExpr1->pRight; assert( pStmt->pExpr1->op==TK_ASSIGN ); assert( pLeft->op==TK_VAR ); sqliteExprCode(pParse, pRight); if( pLeft->flags==EP_NotNull ){ i = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_NotNull, -1, i); sqliteVdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort, "attempt to store null in non-null var", P3_STATIC); sqliteVdbeResolveLabel(v, i); } sqliteVdbeAddOp(v, OP_MemStore, pLeft->iColumn, 1); break; } case TK_BLOCK:{ if( sqliteCompileBlock(pParse, pStmt->pBlock) ){ return 1; } break; } case TK_CASE:{ int jumpInst, addr; int nStmt; int searched; nStmt = pStmt->pStmt1->nStmt; searched = pStmt->pExpr1==0; assert( nStmt>0 ); j = sqliteVdbeMakeLabel(v); if( !searched ){ sqliteExprCode(pParse, pStmt->pExpr1); } for(i=0; i<nStmt; i++){ Stmt *pWhen = pStmt->pStmt1->a[i].pStmt; assert( pWhen->op==TK_WHEN ); if( sqliteExprProcResolve(pParse, b, pWhen->pExpr1) ){ return 1; } if( sqliteExprCheck(pParse, pWhen->pExpr1, 0, 0) ){ return 1; } sqliteExprCode(pParse, pWhen->pExpr1); if( !searched ){ sqliteVdbeAddOp(v, OP_Dup, 1, 1); jumpInst = sqliteVdbeAddOp(v, OP_Ne, 1, 0); }else{ jumpInst = sqliteVdbeAddOp(v, OP_IfNot, 1, 0); } if( sqliteCompileList(pParse, b, pWhen->pStmt1, &skipgoto, 0) ){ return 1; } if( !skipgoto ) { sqliteVdbeAddOp(v, OP_Goto, 0, j); } addr = sqliteVdbeCurrentAddr(v); sqliteVdbeChangeP2(v, jumpInst, addr); } if( !searched ){ sqliteVdbeAddOp(v, OP_Pop, 1, 0); } if( pStmt->pStmt2 ){ assert( pStmt->pStmt2->op==TK_ELSE ); if( sqliteCompileList(pParse, b, pStmt->pStmt2->pStmt1, tailgoto, 0) ){ return 1; } }else{ sqliteVdbeOp3(v, OP_Raise, 0, 0, "CASE_NOT_FOUND", P3_STATIC); if( tailgoto ) *tailgoto = 1; } sqliteVdbeResolveLabel(v, j); break; } case TK_EXIT:{ if( pParse->iLoopExit==0 ) { sqliteErrorMsg(pParse, "EXIT used outside loop statement", 0); return 1; } if( pStmt->pExpr1 ) { sqliteExprCode(pParse, pStmt->pExpr1); sqliteVdbeAddOp(v, OP_If, 1, pParse->iLoopExit); } else { sqliteVdbeAddOp(v, OP_Goto, 0, pParse->iLoopExit); if( tailgoto ) *tailgoto = 1; } break; } case TK_FOR:{ Expr *pLow = pStmt->pExpr1->pRight->pLeft; Expr *pHigh = pStmt->pExpr1->pRight->pRight; int iCounter, iHigh, iPrevExit; assert( pStmt->pExpr1->op==TK_ASSIGN ); assert( pStmt->pExpr1->pLeft->op==TK_VAR ); assert( pStmt->pExpr1->pRight->op==TK_FOR ); iCounter = pParse->nMem-1; iHigh = pParse->nMem++; sqliteExprCode(pParse, pLow); sqliteVdbeAddOp(v, OP_MemStore, iCounter, 1); sqliteExprCode(pParse, pHigh); sqliteVdbeAddOp(v, OP_MemStore, iHigh, 1); sqliteVdbeAddOp(v, OP_MemLoad, iCounter, 0); i = sqliteVdbeCurrentAddr(v); sqliteVdbeAddOp(v, OP_MemLoad, iHigh, 0); iPrevExit = pParse->iLoopExit; pParse->iLoopExit = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Gt, 1, pParse->iLoopExit); if( sqliteCompileList(pParse, b, pStmt->pStmt1, 0, 0) ){ return 1; } sqliteVdbeAddOp(v, OP_MemLoad, iCounter, 0); sqliteVdbeAddOp(v, OP_Integer, 1, 0); sqliteVdbeAddOp(v, OP_Add, 0, 0); sqliteVdbeAddOp(v, OP_MemStore, iCounter, 0); sqliteVdbeAddOp(v, OP_Goto, 0, i); sqliteVdbeResolveLabel(v, pParse->iLoopExit); pParse->iLoopExit = iPrevExit; hideVar(b, iCounter); break; } case TK_IF: { i = sqliteVdbeMakeLabel(v); j = sqliteVdbeMakeLabel(v); sqliteExprCode(pParse, pStmt->pExpr1); sqliteVdbeAddOp(v, OP_IfNot, 1, j); if( sqliteCompileList(pParse, b, pStmt->pStmt1, &skipgoto, 0) ){ return 1; } while( pStmt->pStmt2 ) { if( !skipgoto ) { sqliteVdbeAddOp(v, OP_Goto, 0, i); } sqliteVdbeResolveLabel(v, j); j = sqliteVdbeMakeLabel(v); pStmt = pStmt->pStmt2; assert( pStmt->op==TK_ELSE || pStmt->op==TK_ELSIF ); if( pStmt->op==TK_ELSIF ) { if( sqliteExprProcResolve(pParse, b, pStmt->pExpr1) ){ return 1; } if( sqliteExprCheck(pParse, pStmt->pExpr1, 0, 0) ){ return 1; } sqliteExprCode(pParse, pStmt->pExpr1); sqliteVdbeAddOp(v, OP_IfNot, 1, j); } if( sqliteCompileList(pParse, b, pStmt->pStmt1, &skipgoto, 0) ){ return 1; } } sqliteVdbeResolveLabel(v, i); sqliteVdbeResolveLabel(v, j); break; } case TK_LOOP:{ int iPrevExit = pParse->iLoopExit; pParse->iLoopExit = sqliteVdbeMakeLabel(v); i = sqliteVdbeCurrentAddr(v); if( sqliteCompileList(pParse, b, pStmt->pStmt1, 0, 0) ){ return 1; } sqliteVdbeAddOp(v, OP_Goto, 0, i); sqliteVdbeResolveLabel(v, pParse->iLoopExit); pParse->iLoopExit = iPrevExit; break; } case TK_NULL:{ break; } case TK_PRINT:{ sqliteExprCode(pParse, pStmt->pExpr1); sqliteVdbeAddOp(v, OP_Print, 0, 0); break; } case TK_PROCEDURE: { Expr *pExpr = pStmt->pExpr1; if( sqliteCompileCall(pParse, &(pExpr->token), pExpr->pList) ) { return 1; } sqliteVdbeAddOp(v, OP_Pop, 1, 0); break; } case TK_RAISE:{ if( pStmt->pExpr1==0 ) { if( !in_excep ) { sqliteErrorMsg(pParse, "RAISE without argument illegal outside exception handler", 0); return 1; } sqliteVdbeOp3(v, OP_Raise, 0, 0, 0, P3_STATIC); } else { char *zName = 0; sqliteSetNString(&zName, pStmt->pExpr1->token.z, pStmt->pExpr1->token.n, 0); sqliteVdbeOp3(v, OP_Raise, 0, 0, zName, P3_DYNAMIC); } if( tailgoto ) *tailgoto = 1; break; } case TK_RETURN:{ sqliteExprCode(pParse, pStmt->pExpr1); sqliteVdbeAddOp(v, OP_MemStore, b->mReturn, 1); sqliteVdbeAddOp(v, OP_Goto, 0, b->nExit); if( tailgoto ) *tailgoto = 1; break; } case TK_SQL:{ sqliteCompileSQLStmt(pParse, b, pStmt->pSql); break; } case TK_WHILE:{ int iPrevExit = pParse->iLoopExit; pParse->iLoopExit = sqliteVdbeMakeLabel(v); i = sqliteVdbeCurrentAddr(v); sqliteExprCode(pParse, pStmt->pExpr1); sqliteVdbeAddOp(v, OP_IfNot, 1, pParse->iLoopExit); if( sqliteCompileList(pParse, b, pStmt->pStmt1, 0, 0) ){ return 1; } sqliteVdbeAddOp(v, OP_Goto, 0, i); sqliteVdbeResolveLabel(v, pParse->iLoopExit); pParse->iLoopExit = iPrevExit; break; } } return 0; }