void sqliteDropProc(Parse *pParse, Token *pName){ Object *pObj; char *zName; Vdbe *v = sqliteGetVdbe(pParse); sqlite *db = pParse->db; zName = sqliteStrNDup(pName->z, pName->n); sqliteDequote(zName); pObj = sqliteHashFind(&(db->aDb[0].objectHash), zName, pName->n+1); if( !pParse->explain && !pObj ){ sqliteErrorMsg(pParse, "no such object: %T", pName); goto dropobject_cleanup; } /* Generate code to destroy the database record of the trigger. */ if( v ){ int base; static VdbeOpList dropObject[] = { { OP_Rewind, 0, ADDR(9), 0}, { OP_String, 0, 0, 0}, /* 1 */ { OP_Column, 0, 1, 0}, { OP_Ne, 0, ADDR(8), 0}, { OP_String, 0, 0, "procedure"}, { OP_Column, 0, 0, 0}, { OP_Ne, 0, ADDR(8), 0}, { OP_Delete, 0, 0, 0}, { OP_Next, 0, ADDR(1), 0}, /* 8 */ }; sqliteBeginWriteOperation(pParse, 0, 0); sqliteOpenMasterTable(v, 0); base = sqliteVdbeAddOpList(v, ArraySize(dropObject), dropObject); sqliteVdbeChangeP3(v, base+1, zName, 0); if( pObj && pObj->iDb==0 ){ sqliteChangeCookie(db, v); } sqliteVdbeAddOp(v, OP_Close, 0, 0); sqliteEndWriteOperation(pParse); } /* * If this is not an "explain", then delete the trigger structure. */ if( !pParse->explain ){ sqliteHashInsert(&(db->aDb[pObj->iDb].objectHash), zName, pName->n+1, 0); sqliteDeleteObject(pObj); } dropobject_cleanup: sqliteFree(zName); }
/* ** Process a DELETE FROM statement. */ void sqliteDeleteFrom( Parse *pParse, /* The parser context */ SrcList *pTabList, /* The table from which we should delete things */ Expr *pWhere /* The WHERE clause. May be null */ ){ Vdbe *v; /* The virtual database engine */ Table *pTab; /* The table from which records will be deleted */ const char *zDb; /* Name of database holding pTab */ int end, addr; /* A couple addresses of generated code */ int i; /* Loop counter */ WhereInfo *pWInfo; /* Information about the WHERE clause */ Index *pIdx; /* For looping over indices of the table */ int iCur; /* VDBE Cursor number for pTab */ sqlite *db; /* Main database structure */ int isView; /* True if attempting to delete from a view */ AuthContext sContext; /* Authorization context */ int row_triggers_exist = 0; /* True if any triggers exist */ int before_triggers; /* True if there are BEFORE triggers */ int after_triggers; /* True if there are AFTER triggers */ int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */ sContext.pParse = 0; if( pParse->nErr || sqlite_malloc_failed ){ pTabList = 0; goto delete_from_cleanup; } db = pParse->db; assert( pTabList->nSrc==1 ); /* Locate the table which we want to delete. This table has to be ** put in an SrcList structure because some of the subroutines we ** will be calling are designed to work with multiple tables and expect ** an SrcList* parameter instead of just a Table* parameter. */ pTab = sqliteSrcListLookup(pParse, pTabList); if( pTab==0 ) goto delete_from_cleanup; before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_DELETE, TK_BEFORE, TK_ROW, 0); after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_DELETE, TK_AFTER, TK_ROW, 0); row_triggers_exist = before_triggers || after_triggers; isView = pTab->pSelect!=0; if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){ goto delete_from_cleanup; } assert( pTab->iDb<db->nDb ); zDb = db->aDb[pTab->iDb].zName; if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ goto delete_from_cleanup; } /* If pTab is really a view, make sure it has been initialized. */ if( isView && sqliteViewGetColumnNames(pParse, pTab) ){ goto delete_from_cleanup; } /* Allocate a cursor used to store the old.* data for a trigger. */ if( row_triggers_exist ){ oldIdx = pParse->nTab++; } /* Resolve the column names in all the expressions. */ assert( pTabList->nSrc==1 ); iCur = pTabList->a[0].iCursor = pParse->nTab++; if( pWhere ){ if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){ goto delete_from_cleanup; } if( sqliteExprCheck(pParse, pWhere, 0, 0) ){ goto delete_from_cleanup; } } /* Start the view context */ if( isView ){ sqliteAuthContextPush(pParse, &sContext, pTab->zName); } /* Begin generating code. */ v = sqliteGetVdbe(pParse); if( v==0 ){ goto delete_from_cleanup; } sqliteBeginWriteOperation(pParse, row_triggers_exist, pTab->iDb); /* If we are trying to delete from a view, construct that view into ** a temporary table. */ if( isView ){ Select *pView = sqliteSelectDup(pTab->pSelect); sqliteSelect(pParse, pView, SRT_TempTable, iCur, 0, 0, 0); sqliteSelectDelete(pView); } /* Initialize the counter of the number of rows deleted, if ** we are counting rows. */ if( db->flags & SQLITE_CountRows ){ sqliteVdbeAddOp(v, OP_Integer, 0, 0); } /* Special case: A DELETE without a WHERE clause deletes everything. ** It is easier just to erase the whole table. Note, however, that ** this means that the row change count will be incorrect. */ if( pWhere==0 && !row_triggers_exist ){ if( db->flags & SQLITE_CountRows ){ /* If counting rows deleted, just count the total number of ** entries in the table. */ int endOfLoop = sqliteVdbeMakeLabel(v); int addr; if( !isView ){ sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum); } sqliteVdbeAddOp(v, OP_Rewind, iCur, sqliteVdbeCurrentAddr(v)+2); addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0); sqliteVdbeAddOp(v, OP_Next, iCur, addr); sqliteVdbeResolveLabel(v, endOfLoop); sqliteVdbeAddOp(v, OP_Close, iCur, 0); } if( !isView ){ sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb); } } } /* The usual case: There is a WHERE clause so we have to scan through ** the table and pick which records to delete. */ else{ /* Begin the database scan */ pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1, 0); if( pWInfo==0 ) goto delete_from_cleanup; /* Remember the key of every item to be deleted. */ sqliteVdbeAddOp(v, OP_ListWrite, 0, 0); if( db->flags & SQLITE_CountRows ){ sqliteVdbeAddOp(v, OP_AddImm, 1, 0); } /* End the database scan loop. */ sqliteWhereEnd(pWInfo); /* Open the pseudo-table used to store OLD if there are triggers. */ if( row_triggers_exist ){ sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0); } /* Delete every item whose key was written to the list during the ** database scan. We have to delete items after the scan is complete ** because deleting an item can change the scan order. */ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); end = sqliteVdbeMakeLabel(v); /* This is the beginning of the delete loop when there are ** row triggers. */ if( row_triggers_exist ){ addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); sqliteVdbeAddOp(v, OP_Dup, 0, 0); if( !isView ){ sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum); } sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0); sqliteVdbeAddOp(v, OP_Recno, iCur, 0); sqliteVdbeAddOp(v, OP_RowData, iCur, 0); sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0); if( !isView ){ sqliteVdbeAddOp(v, OP_Close, iCur, 0); } sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, addr); } if( !isView ){ /* Open cursors for the table we are deleting from and all its ** indices. If there are row triggers, this happens inside the ** OP_ListRead loop because the cursor have to all be closed ** before the trigger fires. If there are no row triggers, the ** cursors are opened only once on the outside the loop. */ pParse->nTab = iCur + 1; sqliteOpenTableAndIndices(pParse, pTab, iCur); /* This is the beginning of the delete loop when there are no ** row triggers */ if( !row_triggers_exist ){ addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); } /* Delete the row */ sqliteGenerateRowDelete(db, v, pTab, iCur, pParse->trigStack==0); } /* If there are row triggers, close all cursors then invoke ** the AFTER triggers */ if( row_triggers_exist ){ if( !isView ){ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ sqliteVdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum); } sqliteVdbeAddOp(v, OP_Close, iCur, 0); } sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, addr); } /* End of the delete loop */ sqliteVdbeAddOp(v, OP_Goto, 0, addr); sqliteVdbeResolveLabel(v, end); sqliteVdbeAddOp(v, OP_ListReset, 0, 0); /* Close the cursors after the loop if there are no row triggers */ if( !row_triggers_exist ){ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ sqliteVdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum); } sqliteVdbeAddOp(v, OP_Close, iCur, 0); pParse->nTab = iCur; } } sqliteVdbeAddOp(v, OP_SetCounts, 0, 0); sqliteEndWriteOperation(pParse); /* ** Return the number of rows that were deleted. */ if( db->flags & SQLITE_CountRows ){ sqliteVdbeAddOp(v, OP_ColumnName, 0, 1); sqliteVdbeChangeP3(v, -1, "rows deleted", P3_STATIC); sqliteVdbeAddOp(v, OP_Callback, 1, 0); } delete_from_cleanup: sqliteAuthContextPop(&sContext); sqliteSrcListDelete(pTabList); sqliteExprDelete(pWhere); return; }
/* ** 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); }
/* ** Add an opcode that includes the p3 value. */ int sqliteVdbeOp3(Vdbe *p, int op, int p1, int p2, const char *zP3, int p3type){ int addr = sqliteVdbeAddOp(p, op, p1, p2); sqliteVdbeChangeP3(p, addr, zP3, p3type); return addr; }
/* ** This routine is called after the body of the procedure has been parsed ** in order to complete the process of building the procedure object. */ void sqliteFinishProc( Parse *pParse, /* Parser context */ Block *pBlock, /* The procedure body */ Token *pAll /* Token that describes the complete CREATE text */ ){ Object *no = 0; /* The object whose construction is finishing up */ sqlite *db = pParse->db; /* The database */ Vdbe *v = sqliteGetVdbe(pParse); if( pParse->nErr || pParse->pNewObject==0 ) goto objectfinish_cleanup; no = pParse->pNewObject; pParse->pNewObject = 0; sqliteCompileBlock(pParse, pBlock); sqliteVdbeAddOp(v, OP_Halt, 0, 0); /* save compiled body code, reset vdbe */ if( !pParse->explain ){ no->nOp = v->nOp; v->nOp = 0; no->aOp = v->aOp; v->aOp = 0; v->nOpAlloc = 0; } DbClearProperty(db, 0, DB_Locked); DbClearProperty(db, 1, DB_Locked); /* if we are not initializing build the sqlite_master entry */ if( !db->init.busy ){ static VdbeOpList insertObj[] = { { OP_NewRecno, 0, 0, 0 }, { OP_String, 0, 0, "procedure" }, { OP_String, 0, 0, 0 }, /* 2: object name */ { OP_String, 0, 0, 0 }, { OP_Integer, 0, 0, 0 }, { OP_String, 0, 0, 0 }, /* 5: SQL */ { OP_MakeRecord, 5, 0, 0 }, { OP_PutIntKey, 0, 0, 0 }, }; int addr; /* Make an entry in the sqlite_master table */ if( v==0 ) goto objectfinish_cleanup; sqliteBeginWriteOperation(pParse, 0, 0); sqliteOpenMasterTable(v, 0); addr = sqliteVdbeAddOpList(v, ArraySize(insertObj), insertObj); sqliteVdbeChangeP3(v, addr+2, no->name, 0); sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n); if( no->iDb==0 ){ sqliteChangeCookie(db, v); } sqliteVdbeAddOp(v, OP_Close, 0, 0); sqliteEndWriteOperation(pParse); } if( !pParse->explain ){ sqliteHashInsert(&db->aDb[no->iDb].objectHash, no->name, strlen(no->name)+1, no); no = 0; } objectfinish_cleanup: sqliteDeleteObject(no); sqliteDeleteObject(pParse->pNewObject); pParse->pNewObject = 0; }
/* ** Drop a trigger given a pointer to that trigger. If nested is false, ** then also generate code to remove the trigger from the SQLITE_MASTER ** table. */ void sqliteDropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){ Table *pTable; Vdbe *v; sqlite *db = pParse->db; assert( pTrigger->iDb<db->nDb ); if( pTrigger->iDb>=2 ){ sqliteErrorMsg(pParse, "triggers may not be removed from " "auxiliary database %s", db->aDb[pTrigger->iDb].zName); return; } pTable = sqliteFindTable(db, pTrigger->table,db->aDb[pTrigger->iTabDb].zName); assert(pTable); assert( pTable->iDb==pTrigger->iDb || pTrigger->iDb==1 ); #ifndef SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_DROP_TRIGGER; const char *zDb = db->aDb[pTrigger->iDb].zName; const char *zTab = SCHEMA_TABLE(pTrigger->iDb); if( pTrigger->iDb ) code = SQLITE_DROP_TEMP_TRIGGER; if( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) || sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ return; } } #endif /* Generate code to destroy the database record of the trigger. */ if( pTable!=0 && !nested && (v = sqliteGetVdbe(pParse))!=0 ){ int base; static VdbeOpList dropTrigger[] = { { OP_Rewind, 0, ADDR(9), 0}, { OP_String, 0, 0, 0}, /* 1 */ { OP_Column, 0, 1, 0}, { OP_Ne, 0, ADDR(8), 0}, { OP_String, 0, 0, "trigger"}, { OP_Column, 0, 0, 0}, { OP_Ne, 0, ADDR(8), 0}, { OP_Delete, 0, 0, 0}, { OP_Next, 0, ADDR(1), 0}, /* 8 */ }; sqliteBeginWriteOperation(pParse, 0, 0); sqliteOpenMasterTable(v, pTrigger->iDb); base = sqliteVdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); sqliteVdbeChangeP3(v, base+1, pTrigger->name, 0); if( pTrigger->iDb==0 ){ sqliteChangeCookie(db, v); } sqliteVdbeAddOp(v, OP_Close, 0, 0); sqliteEndWriteOperation(pParse); } /* * If this is not an "explain", then delete the trigger structure. */ if( !pParse->explain ){ const char *zName = pTrigger->name; int nName = strlen(zName); if( pTable->pTrigger == pTrigger ){ pTable->pTrigger = pTrigger->pNext; }else{ Trigger *cc = pTable->pTrigger; while( cc ){ if( cc->pNext == pTrigger ){ cc->pNext = cc->pNext->pNext; break; } cc = cc->pNext; } assert(cc); } sqliteHashInsert(&(db->aDb[pTrigger->iDb].trigHash), zName, nName+1, 0); sqliteDeleteTrigger(pTrigger); } }
/* ** This routine is called after all of the trigger actions have been parsed ** in order to complete the process of building the trigger. */ void sqliteFinishTrigger( Parse *pParse, /* Parser context */ TriggerStep *pStepList, /* The triggered program */ Token *pAll /* Token that describes the complete CREATE TRIGGER */ ){ Trigger *nt = 0; /* The trigger whose construction is finishing up */ sqlite *db = pParse->db; /* The database */ DbFixer sFix; if( pParse->nErr || pParse->pNewTrigger==0 ) goto triggerfinish_cleanup; nt = pParse->pNewTrigger; pParse->pNewTrigger = 0; nt->step_list = pStepList; while( pStepList ){ pStepList->pTrig = nt; pStepList = pStepList->pNext; } if( sqliteFixInit(&sFix, pParse, nt->iDb, "trigger", &nt->nameToken) && sqliteFixTriggerStep(&sFix, nt->step_list) ){ goto triggerfinish_cleanup; } /* if we are not initializing, and this trigger is not on a TEMP table, ** build the sqlite_master entry */ if( !db->init.busy ){ static VdbeOpList insertTrig[] = { { OP_NewRecno, 0, 0, 0 }, { OP_String, 0, 0, "trigger" }, { OP_String, 0, 0, 0 }, /* 2: trigger name */ { OP_String, 0, 0, 0 }, /* 3: table name */ { OP_Integer, 0, 0, 0 }, { OP_String, 0, 0, 0 }, /* 5: SQL */ { OP_MakeRecord, 5, 0, 0 }, { OP_PutIntKey, 0, 0, 0 }, }; int addr; Vdbe *v; /* Make an entry in the sqlite_master table */ v = sqliteGetVdbe(pParse); if( v==0 ) goto triggerfinish_cleanup; sqliteBeginWriteOperation(pParse, 0, 0); sqliteOpenMasterTable(v, nt->iDb); addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig); sqliteVdbeChangeP3(v, addr+2, nt->name, 0); sqliteVdbeChangeP3(v, addr+3, nt->table, 0); sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n); if( nt->iDb==0 ){ sqliteChangeCookie(db, v); } sqliteVdbeAddOp(v, OP_Close, 0, 0); sqliteEndWriteOperation(pParse); } if( !pParse->explain ){ Table *pTab; sqliteHashInsert(&db->aDb[nt->iDb].trigHash, nt->name, strlen(nt->name)+1, nt); pTab = sqliteLocateTable(pParse, nt->table, db->aDb[nt->iTabDb].zName); assert( pTab!=0 ); nt->pNext = pTab->pTrigger; pTab->pTrigger = nt; nt = 0; } triggerfinish_cleanup: sqliteDeleteTrigger(nt); sqliteDeleteTrigger(pParse->pNewTrigger); pParse->pNewTrigger = 0; sqliteDeleteTriggerStep(pStepList); }
/* ** 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 */ SrcList *pTableName, /* The name of the table into which we will insert */ Token *pFilename, /* The file from which to obtain information */ Token *pDelimiter, /* Use this as the field delimiter */ int onError /* What to do if a constraint fails */ ){ Table *pTab; int i; Vdbe *v; int addr, end; char *zFile = 0; const char *zDb; sqlite *db = pParse->db; if( sqlite_malloc_failed ) goto copy_cleanup; assert( pTableName->nSrc==1 ); pTab = sqliteSrcListLookup(pParse, pTableName); if( pTab==0 || sqliteIsReadOnly(pParse, pTab, 0) ) goto copy_cleanup; zFile = sqliteStrNDup(pFilename->z, pFilename->n); sqliteDequote(zFile); assert( pTab->iDb<db->nDb ); zDb = db->aDb[pTab->iDb].zName; if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) || sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile, zDb) ){ goto copy_cleanup; } v = sqliteGetVdbe(pParse); if( v ){ sqliteBeginWriteOperation(pParse, 1, pTab->iDb); addr = sqliteVdbeOp3(v, OP_FileOpen, 0, 0, pFilename->z, pFilename->n); sqliteVdbeDequoteP3(v, addr); sqliteOpenTableAndIndices(pParse, pTab, 0); 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, pTab->iPKey>=0, 0, onError, addr); sqliteCompleteInsertion(pParse, pTab, 0, 0, 0, 0, -1); 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, 1); sqliteVdbeChangeP3(v, -1, "rows inserted", P3_STATIC); sqliteVdbeAddOp(v, OP_Callback, 1, 0); } } copy_cleanup: sqliteSrcListDelete(pTableName); sqliteFree(zFile); return; }
/* ** Generate code to do a constraint check prior to an INSERT or an UPDATE. ** ** When this routine is called, the stack contains (from bottom to top) ** the following values: ** ** 1. The recno of the row to be updated before the update. This ** value is omitted unless we are doing an UPDATE that involves a ** change to the record number. ** ** 2. The recno of the row after the update. ** ** 3. The data in the first column of the entry after the update. ** ** i. Data from middle columns... ** ** N. The data in the last column of the entry after the update. ** ** The old recno shown as entry (1) above is omitted unless both isUpdate ** and recnoChng are 1. isUpdate is true for UPDATEs and false for ** INSERTs and recnoChng is true if the record number is being changed. ** ** The code generated by this routine pushes additional entries onto ** the stack which are the keys for new index entries for the new record. ** The order of index keys is the same as the order of the indices on ** the pTable->pIndex list. A key is only created for index i if ** aIdxUsed!=0 and aIdxUsed[i]!=0. ** ** This routine also generates code to check constraints. NOT NULL, ** CHECK, and UNIQUE constraints are all checked. If a constraint fails, ** then the appropriate action is performed. There are five possible ** actions: ROLLBACK, ABORT, FAIL, REPLACE, and IGNORE. ** ** Constraint type Action What Happens ** --------------- ---------- ---------------------------------------- ** any ROLLBACK The current transaction is rolled back and ** sqlite_exec() returns immediately with a ** return code of SQLITE_CONSTRAINT. ** ** any ABORT Back out changes from the current command ** only (do not do a complete rollback) then ** cause sqlite_exec() to return immediately ** with SQLITE_CONSTRAINT. ** ** any FAIL Sqlite_exec() returns immediately with a ** return code of SQLITE_CONSTRAINT. The ** transaction is not rolled back and any ** prior changes are retained. ** ** any IGNORE The record number and data is popped from ** the stack and there is an immediate jump ** to label ignoreDest. ** ** NOT NULL REPLACE The NULL value is replace by the default ** value for that column. If the default value ** is NULL, the action is the same as ABORT. ** ** UNIQUE REPLACE The other row that conflicts with the row ** being inserted is removed. ** ** CHECK REPLACE Illegal. The results in an exception. ** ** Which action to take is determined by the overrideError parameter. ** Or if overrideError==OE_Default, then the pParse->onError parameter ** is used. Or if pParse->onError==OE_Default then the onError value ** for the constraint is used. ** ** The calling routine must open a read/write cursor for pTab with ** cursor number "base". All indices of pTab must also have open ** read/write cursors with cursor number base+i for the i-th cursor. ** Except, if there is no possibility of a REPLACE action then ** cursors do not need to be open for indices where aIdxUsed[i]==0. ** ** If the isUpdate flag is true, it means that the "base" cursor is ** initially pointing to an entry that is being updated. The isUpdate ** flag causes extra code to be generated so that the "base" cursor ** is still pointing at the same entry after the routine returns. ** Without the isUpdate flag, the "base" cursor might be moved. */ void sqliteGenerateConstraintChecks( Parse *pParse, /* The parser context */ Table *pTab, /* the table into which we are inserting */ int base, /* Index of a read/write cursor pointing at pTab */ char *aIdxUsed, /* Which indices are used. NULL means all are used */ int recnoChng, /* True if the record number will change */ int isUpdate, /* True for UPDATE, False for INSERT */ int overrideError, /* Override onError to this if not OE_Default */ int ignoreDest /* Jump to this label on an OE_Ignore resolution */ ){ int i; Vdbe *v; int nCol; int onError; int addr; int extra; int iCur; Index *pIdx; int seenReplace = 0; int jumpInst1, jumpInst2; int contAddr; int hasTwoRecnos = (isUpdate && recnoChng); v = sqliteGetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ nCol = pTab->nCol; /* Test all NOT NULL constraints. */ for(i=0; i<nCol; i++){ if( i==pTab->iPKey ){ continue; } onError = pTab->aCol[i].notNull; if( onError==OE_None ) continue; if( overrideError!=OE_Default ){ onError = overrideError; }else if( pParse->db->onError!=OE_Default ){ onError = pParse->db->onError; }else if( onError==OE_Default ){ onError = OE_Abort; } if( onError==OE_Replace && pTab->aCol[i].zDflt==0 ){ onError = OE_Abort; } sqliteVdbeAddOp(v, OP_Dup, nCol-1-i, 1); addr = sqliteVdbeAddOp(v, OP_NotNull, 1, 0); switch( onError ){ case OE_Rollback: case OE_Abort: case OE_Fail: { char *zMsg = 0; sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError); sqliteSetString(&zMsg, pTab->zName, ".", pTab->aCol[i].zName, " may not be NULL", (char*)0); sqliteVdbeChangeP3(v, -1, zMsg, P3_DYNAMIC); break; } case OE_Ignore: { sqliteVdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0); sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest); break; } case OE_Replace: { sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zDflt, P3_STATIC); sqliteVdbeAddOp(v, OP_Push, nCol-i, 0); break; } default: assert(0); } sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v)); } /* Test all CHECK constraints */ /**** TBD ****/ /* If we have an INTEGER PRIMARY KEY, make sure the primary key ** of the new record does not previously exist. Except, if this ** is an UPDATE and the primary key is not changing, that is OK. */ if( recnoChng ){ onError = pTab->keyConf; if( overrideError!=OE_Default ){ onError = overrideError; }else if( pParse->db->onError!=OE_Default ){ onError = pParse->db->onError; }else if( onError==OE_Default ){ onError = OE_Abort; } if( isUpdate ){ sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1); sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1); jumpInst1 = sqliteVdbeAddOp(v, OP_Eq, 0, 0); } sqliteVdbeAddOp(v, OP_Dup, nCol, 1); jumpInst2 = sqliteVdbeAddOp(v, OP_NotExists, base, 0); switch( onError ){ default: { onError = OE_Abort; /* Fall thru into the next case */ } case OE_Rollback: case OE_Abort: case OE_Fail: { sqliteVdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError, "PRIMARY KEY must be unique", P3_STATIC); break; } case OE_Replace: { sqliteGenerateRowIndexDelete(pParse->db, v, pTab, base, 0); if( isUpdate ){ sqliteVdbeAddOp(v, OP_Dup, nCol+hasTwoRecnos, 1); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); } seenReplace = 1; break; } case OE_Ignore: { assert( seenReplace==0 ); sqliteVdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0); sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest); break; } } contAddr = sqliteVdbeCurrentAddr(v); sqliteVdbeChangeP2(v, jumpInst2, contAddr); if( isUpdate ){ sqliteVdbeChangeP2(v, jumpInst1, contAddr); sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); } } /* Test all UNIQUE constraints by creating entries for each UNIQUE ** index and making sure that duplicate entries do not already exist. ** Add the new records to the indices as we go. */ extra = -1; for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ if( aIdxUsed && aIdxUsed[iCur]==0 ) continue; /* Skip unused indices */ extra++; /* Create a key for accessing the index entry */ sqliteVdbeAddOp(v, OP_Dup, nCol+extra, 1); for(i=0; i<pIdx->nColumn; i++){ int idx = pIdx->aiColumn[i]; if( idx==pTab->iPKey ){ sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol+1, 1); }else{ sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 1); } } jumpInst1 = sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0); if( pParse->db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx); /* Find out what action to take in case there is an indexing conflict */ onError = pIdx->onError; if( onError==OE_None ) continue; /* pIdx is not a UNIQUE index */ if( overrideError!=OE_Default ){ onError = overrideError; }else if( pParse->db->onError!=OE_Default ){ onError = pParse->db->onError; }else if( onError==OE_Default ){ onError = OE_Abort; } if( seenReplace ){ if( onError==OE_Ignore ) onError = OE_Replace; else if( onError==OE_Fail ) onError = OE_Abort; } /* Check to see if the new index entry will be unique */ sqliteVdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRecnos, 1); jumpInst2 = sqliteVdbeAddOp(v, OP_IsUnique, base+iCur+1, 0); /* Generate code that executes if the new index entry is not unique */ switch( onError ){ case OE_Rollback: case OE_Abort: case OE_Fail: { int j, n1, n2; char zErrMsg[200]; strcpy(zErrMsg, pIdx->nColumn>1 ? "columns " : "column "); n1 = strlen(zErrMsg); for(j=0; j<pIdx->nColumn && n1<sizeof(zErrMsg)-30; j++){ char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName; n2 = strlen(zCol); if( j>0 ){ strcpy(&zErrMsg[n1], ", "); n1 += 2; } if( n1+n2>sizeof(zErrMsg)-30 ){ strcpy(&zErrMsg[n1], "..."); n1 += 3; break; }else{ strcpy(&zErrMsg[n1], zCol); n1 += n2; } } strcpy(&zErrMsg[n1], pIdx->nColumn>1 ? " are not unique" : " is not unique"); sqliteVdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError, zErrMsg, 0); break; } case OE_Ignore: { assert( seenReplace==0 ); sqliteVdbeAddOp(v, OP_Pop, nCol+extra+3+hasTwoRecnos, 0); sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest); break; } case OE_Replace: { sqliteGenerateRowDelete(pParse->db, v, pTab, base, 0); if( isUpdate ){ sqliteVdbeAddOp(v, OP_Dup, nCol+extra+1+hasTwoRecnos, 1); sqliteVdbeAddOp(v, OP_MoveTo, base, 0); } seenReplace = 1; break; } default: assert(0); } contAddr = sqliteVdbeCurrentAddr(v); #if NULL_DISTINCT_FOR_UNIQUE sqliteVdbeChangeP2(v, jumpInst1, contAddr); #endif sqliteVdbeChangeP2(v, jumpInst2, contAddr); } }
/* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an (opaque) structure that contains ** information needed to terminate the loop. Later, the calling routine ** should invoke sqliteWhereEnd() with the return value of this function ** in order to complete the WHERE clause processing. ** ** If an error occurs, this routine returns NULL. ** ** The basic idea is to do a nested loop, one loop for each table in ** the FROM clause of a select. (INSERT and UPDATE statements are the ** same as a SELECT with only a single table in the FROM clause.) For ** example, if the SQL is this: ** ** SELECT * FROM t1, t2, t3 WHERE ...; ** ** Then the code generated is conceptually like the following: ** ** foreach row1 in t1 do \ Code generated ** foreach row2 in t2 do |-- by sqliteWhereBegin() ** foreach row3 in t3 do / ** ... ** end \ Code generated ** end |-- by sqliteWhereEnd() ** end / ** ** There are Btree cursors associated with each table. t1 uses cursor ** number pTabList->a[0].iCursor. t2 uses the cursor pTabList->a[1].iCursor. ** And so forth. This routine generates code to open those VDBE cursors ** and sqliteWhereEnd() generates the code to close them. ** ** If the WHERE clause is empty, the foreach loops must each scan their ** entire tables. Thus a three-way join is an O(N^3) operation. But if ** the tables have indices and there are terms in the WHERE clause that ** refer to those indices, a complete table scan can be avoided and the ** code will run much faster. Most of the work of this routine is checking ** to see if there are indices that can be used to speed up the loop. ** ** Terms of the WHERE clause are also used to limit which rows actually ** make it to the "..." in the middle of the loop. After each "foreach", ** terms of the WHERE clause that use only terms in that loop and outer ** loops are evaluated and if false a jump is made around all subsequent ** inner loops (or around the "..." if the test occurs within the inner- ** most loop) ** ** OUTER JOINS ** ** An outer join of tables t1 and t2 is conceptally coded as follows: ** ** foreach row1 in t1 do ** flag = 0 ** foreach row2 in t2 do ** start: ** ... ** flag = 1 ** end ** if flag==0 then ** move the row2 cursor to a null row ** goto start ** fi ** end ** ** ORDER BY CLAUSE PROCESSING ** ** *ppOrderBy is a pointer to the ORDER BY clause of a SELECT statement, ** if there is one. If there is no ORDER BY clause or if this routine ** is called from an UPDATE or DELETE statement, then ppOrderBy is NULL. ** ** If an index can be used so that the natural output order of the table ** scan is correct for the ORDER BY clause, then that index is used and ** *ppOrderBy is set to NULL. This is an optimization that prevents an ** unnecessary sort of the result set if an index appropriate for the ** ORDER BY clause already exists. ** ** If the where clause loops cannot be arranged to provide the correct ** output order, then the *ppOrderBy is unchanged. */ WhereInfo *sqliteWhereBegin( Parse *pParse, /* The parser context */ SrcList *pTabList, /* A list of all tables to be scanned */ Expr *pWhere, /* The WHERE clause */ int pushKey, /* If TRUE, leave the table key on the stack */ ExprList **ppOrderBy /* An ORDER BY clause, or NULL */ ) { int i; /* Loop counter */ WhereInfo *pWInfo; /* Will become the return value of this function */ Vdbe *v = pParse->pVdbe; /* The virtual database engine */ int brk, cont = 0; /* Addresses used during code generation */ int nExpr; /* Number of subexpressions in the WHERE clause */ int loopMask; /* One bit set for each outer loop */ int haveKey; /* True if KEY is on the stack */ ExprMaskSet maskSet; /* The expression mask set */ int iDirectEq[32]; /* Term of the form ROWID==X for the N-th table */ int iDirectLt[32]; /* Term of the form ROWID<X or ROWID<=X */ int iDirectGt[32]; /* Term of the form ROWID>X or ROWID>=X */ ExprInfo aExpr[101]; /* The WHERE clause is divided into these expressions */ /* pushKey is only allowed if there is a single table (as in an INSERT or ** UPDATE statement) */ assert( pushKey==0 || pTabList->nSrc==1 ); /* Split the WHERE clause into separate subexpressions where each ** subexpression is separated by an AND operator. If the aExpr[] ** array fills up, the last entry might point to an expression which ** contains additional unfactored AND operators. */ initMaskSet(&maskSet); memset(aExpr, 0, sizeof(aExpr)); nExpr = exprSplit(ARRAYSIZE(aExpr), aExpr, pWhere); if( nExpr==ARRAYSIZE(aExpr) ) { char zBuf[50]; sprintf(zBuf, "%d", (int)ARRAYSIZE(aExpr)-1); sqliteSetString(&pParse->zErrMsg, "WHERE clause too complex - no more " "than ", zBuf, " terms allowed", (char*)0); pParse->nErr++; return 0; } /* Allocate and initialize the WhereInfo structure that will become the ** return value. */ pWInfo = sqliteMalloc( sizeof(WhereInfo) + pTabList->nSrc*sizeof(WhereLevel)); if( sqlite_malloc_failed ) { sqliteFree(pWInfo); return 0; } pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; pWInfo->peakNTab = pWInfo->savedNTab = pParse->nTab; pWInfo->iBreak = sqliteVdbeMakeLabel(v); /* Special case: a WHERE clause that is constant. Evaluate the ** expression and either jump over all of the code or fall thru. */ if( pWhere && (pTabList->nSrc==0 || sqliteExprIsConstant(pWhere)) ) { sqliteExprIfFalse(pParse, pWhere, pWInfo->iBreak, 1); pWhere = 0; } /* Analyze all of the subexpressions. */ for(i=0; i<nExpr; i++) { exprAnalyze(&maskSet, &aExpr[i]); /* If we are executing a trigger body, remove all references to ** new.* and old.* tables from the prerequisite masks. */ if( pParse->trigStack ) { int x; if( (x = pParse->trigStack->newIdx) >= 0 ) { int mask = ~getMask(&maskSet, x); aExpr[i].prereqRight &= mask; aExpr[i].prereqLeft &= mask; aExpr[i].prereqAll &= mask; } if( (x = pParse->trigStack->oldIdx) >= 0 ) { int mask = ~getMask(&maskSet, x); aExpr[i].prereqRight &= mask; aExpr[i].prereqLeft &= mask; aExpr[i].prereqAll &= mask; } } } /* Figure out what index to use (if any) for each nested loop. ** Make pWInfo->a[i].pIdx point to the index to use for the i-th nested ** loop where i==0 is the outer loop and i==pTabList->nSrc-1 is the inner ** loop. ** ** If terms exist that use the ROWID of any table, then set the ** iDirectEq[], iDirectLt[], or iDirectGt[] elements for that table ** to the index of the term containing the ROWID. We always prefer ** to use a ROWID which can directly access a table rather than an ** index which requires reading an index first to get the rowid then ** doing a second read of the actual database table. ** ** Actually, if there are more than 32 tables in the join, only the ** first 32 tables are candidates for indices. This is (again) due ** to the limit of 32 bits in an integer bitmask. */ loopMask = 0; for(i=0; i<pTabList->nSrc && i<ARRAYSIZE(iDirectEq); i++) { int j; int iCur = pTabList->a[i].iCursor; /* The cursor for this table */ int mask = getMask(&maskSet, iCur); /* Cursor mask for this table */ Table *pTab = pTabList->a[i].pTab; Index *pIdx; Index *pBestIdx = 0; int bestScore = 0; /* Check to see if there is an expression that uses only the ** ROWID field of this table. For terms of the form ROWID==expr ** set iDirectEq[i] to the index of the term. For terms of the ** form ROWID<expr or ROWID<=expr set iDirectLt[i] to the term index. ** For terms like ROWID>expr or ROWID>=expr set iDirectGt[i]. ** ** (Added:) Treat ROWID IN expr like ROWID=expr. */ pWInfo->a[i].iCur = -1; iDirectEq[i] = -1; iDirectLt[i] = -1; iDirectGt[i] = -1; for(j=0; j<nExpr; j++) { if( aExpr[j].idxLeft==iCur && aExpr[j].p->pLeft->iColumn<0 && (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ) { switch( aExpr[j].p->op ) { case TK_IN: case TK_EQ: iDirectEq[i] = j; break; case TK_LE: case TK_LT: iDirectLt[i] = j; break; case TK_GE: case TK_GT: iDirectGt[i] = j; break; } } if( aExpr[j].idxRight==iCur && aExpr[j].p->pRight->iColumn<0 && (aExpr[j].prereqLeft & loopMask)==aExpr[j].prereqLeft ) { switch( aExpr[j].p->op ) { case TK_EQ: iDirectEq[i] = j; break; case TK_LE: case TK_LT: iDirectGt[i] = j; break; case TK_GE: case TK_GT: iDirectLt[i] = j; break; } } } if( iDirectEq[i]>=0 ) { loopMask |= mask; pWInfo->a[i].pIdx = 0; continue; } /* Do a search for usable indices. Leave pBestIdx pointing to ** the "best" index. pBestIdx is left set to NULL if no indices ** are usable. ** ** The best index is determined as follows. For each of the ** left-most terms that is fixed by an equality operator, add ** 8 to the score. The right-most term of the index may be ** constrained by an inequality. Add 1 if for an "x<..." constraint ** and add 2 for an "x>..." constraint. Chose the index that ** gives the best score. ** ** This scoring system is designed so that the score can later be ** used to determine how the index is used. If the score&7 is 0 ** then all constraints are equalities. If score&1 is not 0 then ** there is an inequality used as a termination key. (ex: "x<...") ** If score&2 is not 0 then there is an inequality used as the ** start key. (ex: "x>..."). A score or 4 is the special case ** of an IN operator constraint. (ex: "x IN ..."). ** ** The IN operator (as in "<expr> IN (...)") is treated the same as ** an equality comparison except that it can only be used on the ** left-most column of an index and other terms of the WHERE clause ** cannot be used in conjunction with the IN operator to help satisfy ** other columns of the index. */ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext) { int eqMask = 0; /* Index columns covered by an x=... term */ int ltMask = 0; /* Index columns covered by an x<... term */ int gtMask = 0; /* Index columns covered by an x>... term */ int inMask = 0; /* Index columns covered by an x IN .. term */ int nEq, m, score; if( pIdx->nColumn>32 ) continue; /* Ignore indices too many columns */ for(j=0; j<nExpr; j++) { if( aExpr[j].idxLeft==iCur && (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ) { int iColumn = aExpr[j].p->pLeft->iColumn; int k; for(k=0; k<pIdx->nColumn; k++) { if( pIdx->aiColumn[k]==iColumn ) { switch( aExpr[j].p->op ) { case TK_IN: { if( k==0 ) inMask |= 1; break; } case TK_EQ: { eqMask |= 1<<k; break; } case TK_LE: case TK_LT: { ltMask |= 1<<k; break; } case TK_GE: case TK_GT: { gtMask |= 1<<k; break; } default: { /* CANT_HAPPEN */ assert( 0 ); break; } } break; } } } if( aExpr[j].idxRight==iCur && (aExpr[j].prereqLeft & loopMask)==aExpr[j].prereqLeft ) { int iColumn = aExpr[j].p->pRight->iColumn; int k; for(k=0; k<pIdx->nColumn; k++) { if( pIdx->aiColumn[k]==iColumn ) { switch( aExpr[j].p->op ) { case TK_EQ: { eqMask |= 1<<k; break; } case TK_LE: case TK_LT: { gtMask |= 1<<k; break; } case TK_GE: case TK_GT: { ltMask |= 1<<k; break; } default: { /* CANT_HAPPEN */ assert( 0 ); break; } } break; } } } } /* The following loop ends with nEq set to the number of columns ** on the left of the index with == constraints. */ for(nEq=0; nEq<pIdx->nColumn; nEq++) { m = (1<<(nEq+1))-1; if( (m & eqMask)!=m ) break; } score = nEq*8; /* Base score is 8 times number of == constraints */ m = 1<<nEq; if( m & ltMask ) score++; /* Increase score for a < constraint */ if( m & gtMask ) score+=2; /* Increase score for a > constraint */ if( score==0 && inMask ) score = 4; /* Default score for IN constraint */ if( score>bestScore ) { pBestIdx = pIdx; bestScore = score; } } pWInfo->a[i].pIdx = pBestIdx; pWInfo->a[i].score = bestScore; pWInfo->a[i].bRev = 0; loopMask |= mask; if( pBestIdx ) { pWInfo->a[i].iCur = pParse->nTab++; pWInfo->peakNTab = pParse->nTab; } } /* Check to see if the ORDER BY clause is or can be satisfied by the ** use of an index on the first table. */ if( ppOrderBy && *ppOrderBy && pTabList->nSrc>0 ) { Index *pSortIdx; Index *pIdx; Table *pTab; int bRev = 0; pTab = pTabList->a[0].pTab; pIdx = pWInfo->a[0].pIdx; if( pIdx && pWInfo->a[0].score==4 ) { /* If there is already an IN index on the left-most table, ** it will not give the correct sort order. ** So, pretend that no suitable index is found. */ pSortIdx = 0; } else if( iDirectEq[0]>=0 || iDirectLt[0]>=0 || iDirectGt[0]>=0 ) { /* If the left-most column is accessed using its ROWID, then do ** not try to sort by index. */ pSortIdx = 0; } else { int nEqCol = (pWInfo->a[0].score+4)/8; pSortIdx = findSortingIndex(pTab, pTabList->a[0].iCursor, *ppOrderBy, pIdx, nEqCol, &bRev); } if( pSortIdx && (pIdx==0 || pIdx==pSortIdx) ) { if( pIdx==0 ) { pWInfo->a[0].pIdx = pSortIdx; pWInfo->a[0].iCur = pParse->nTab++; pWInfo->peakNTab = pParse->nTab; } pWInfo->a[0].bRev = bRev; *ppOrderBy = 0; } } /* Open all tables in the pTabList and all indices used by those tables. */ for(i=0; i<pTabList->nSrc; i++) { Table *pTab; pTab = pTabList->a[i].pTab; if( pTab->isTransient || pTab->pSelect ) continue; sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, pTabList->a[i].iCursor, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); sqliteCodeVerifySchema(pParse, pTab->iDb); if( pWInfo->a[i].pIdx!=0 ) { sqliteVdbeAddOp(v, OP_Integer, pWInfo->a[i].pIdx->iDb, 0); sqliteVdbeAddOp(v, OP_OpenRead, pWInfo->a[i].iCur, pWInfo->a[i].pIdx->tnum); sqliteVdbeChangeP3(v, -1, pWInfo->a[i].pIdx->zName, P3_STATIC); } } /* Generate the code to do the search */ loopMask = 0; for(i=0; i<pTabList->nSrc; i++) { int j, k; int iCur = pTabList->a[i].iCursor; Index *pIdx; WhereLevel *pLevel = &pWInfo->a[i]; /* If this is the right table of a LEFT OUTER JOIN, allocate and ** initialize a memory cell that records if this table matches any ** row of the left table of the join. */ if( i>0 && (pTabList->a[i-1].jointype & JT_LEFT)!=0 ) { if( !pParse->nMem ) pParse->nMem++; pLevel->iLeftJoin = pParse->nMem++; sqliteVdbeAddOp(v, OP_String, 0, 0); sqliteVdbeAddOp(v, OP_MemStore, pLevel->iLeftJoin, 1); } pIdx = pLevel->pIdx; pLevel->inOp = OP_Noop; if( i<ARRAYSIZE(iDirectEq) && iDirectEq[i]>=0 ) { /* Case 1: We can directly reference a single row using an ** equality comparison against the ROWID field. Or ** we reference multiple rows using a "rowid IN (...)" ** construct. */ k = iDirectEq[i]; assert( k<nExpr ); assert( aExpr[k].p!=0 ); assert( aExpr[k].idxLeft==iCur || aExpr[k].idxRight==iCur ); brk = pLevel->brk = sqliteVdbeMakeLabel(v); if( aExpr[k].idxLeft==iCur ) { Expr *pX = aExpr[k].p; if( pX->op!=TK_IN ) { sqliteExprCode(pParse, aExpr[k].p->pRight); } else if( pX->pList ) { sqliteVdbeAddOp(v, OP_SetFirst, pX->iTable, brk); pLevel->inOp = OP_SetNext; pLevel->inP1 = pX->iTable; pLevel->inP2 = sqliteVdbeCurrentAddr(v); } else { assert( pX->pSelect ); sqliteVdbeAddOp(v, OP_Rewind, pX->iTable, brk); sqliteVdbeAddOp(v, OP_KeyAsData, pX->iTable, 1); pLevel->inP2 = sqliteVdbeAddOp(v, OP_FullKey, pX->iTable, 0); pLevel->inOp = OP_Next; pLevel->inP1 = pX->iTable; } } else { sqliteExprCode(pParse, aExpr[k].p->pLeft); } aExpr[k].p = 0; cont = pLevel->cont = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_MustBeInt, 1, brk); haveKey = 0; sqliteVdbeAddOp(v, OP_NotExists, iCur, brk); pLevel->op = OP_Noop; } else if( pIdx!=0 && pLevel->score>0 && pLevel->score%4==0 ) { /* Case 2: There is an index and all terms of the WHERE clause that ** refer to the index use the "==" or "IN" operators. */ int start; int testOp; int nColumn = (pLevel->score+4)/8; brk = pLevel->brk = sqliteVdbeMakeLabel(v); for(j=0; j<nColumn; j++) { for(k=0; k<nExpr; k++) { Expr *pX = aExpr[k].p; if( pX==0 ) continue; if( aExpr[k].idxLeft==iCur && (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight && pX->pLeft->iColumn==pIdx->aiColumn[j] ) { if( pX->op==TK_EQ ) { sqliteExprCode(pParse, pX->pRight); aExpr[k].p = 0; break; } if( pX->op==TK_IN && nColumn==1 ) { if( pX->pList ) { sqliteVdbeAddOp(v, OP_SetFirst, pX->iTable, brk); pLevel->inOp = OP_SetNext; pLevel->inP1 = pX->iTable; pLevel->inP2 = sqliteVdbeCurrentAddr(v); } else { assert( pX->pSelect ); sqliteVdbeAddOp(v, OP_Rewind, pX->iTable, brk); sqliteVdbeAddOp(v, OP_KeyAsData, pX->iTable, 1); pLevel->inP2 = sqliteVdbeAddOp(v, OP_FullKey, pX->iTable, 0); pLevel->inOp = OP_Next; pLevel->inP1 = pX->iTable; } aExpr[k].p = 0; break; } } if( aExpr[k].idxRight==iCur && aExpr[k].p->op==TK_EQ && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft && aExpr[k].p->pRight->iColumn==pIdx->aiColumn[j] ) { sqliteExprCode(pParse, aExpr[k].p->pLeft); aExpr[k].p = 0; break; } } } pLevel->iMem = pParse->nMem++; cont = pLevel->cont = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_NotNull, -nColumn, sqliteVdbeCurrentAddr(v)+3); sqliteVdbeAddOp(v, OP_Pop, nColumn, 0); sqliteVdbeAddOp(v, OP_Goto, 0, brk); sqliteVdbeAddOp(v, OP_MakeKey, nColumn, 0); sqliteAddIdxKeyType(v, pIdx); if( nColumn==pIdx->nColumn || pLevel->bRev ) { sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 0); testOp = OP_IdxGT; } else { sqliteVdbeAddOp(v, OP_Dup, 0, 0); sqliteVdbeAddOp(v, OP_IncrKey, 0, 0); sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 1); testOp = OP_IdxGE; } if( pLevel->bRev ) { /* Scan in reverse order */ sqliteVdbeAddOp(v, OP_IncrKey, 0, 0); sqliteVdbeAddOp(v, OP_MoveLt, pLevel->iCur, brk); start = sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0); sqliteVdbeAddOp(v, OP_IdxLT, pLevel->iCur, brk); pLevel->op = OP_Prev; } else { /* Scan in the forward order */ sqliteVdbeAddOp(v, OP_MoveTo, pLevel->iCur, brk); start = sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0); sqliteVdbeAddOp(v, testOp, pLevel->iCur, brk); pLevel->op = OP_Next; } sqliteVdbeAddOp(v, OP_RowKey, pLevel->iCur, 0); sqliteVdbeAddOp(v, OP_IdxIsNull, nColumn, cont); sqliteVdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0); if( i==pTabList->nSrc-1 && pushKey ) { haveKey = 1; } else { sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0); haveKey = 0; } pLevel->p1 = pLevel->iCur; pLevel->p2 = start; } else if( i<ARRAYSIZE(iDirectLt) && (iDirectLt[i]>=0 || iDirectGt[i]>=0) ) { /* Case 3: We have an inequality comparison against the ROWID field. */ int testOp = OP_Noop; int start; brk = pLevel->brk = sqliteVdbeMakeLabel(v); cont = pLevel->cont = sqliteVdbeMakeLabel(v); if( iDirectGt[i]>=0 ) { k = iDirectGt[i]; assert( k<nExpr ); assert( aExpr[k].p!=0 ); assert( aExpr[k].idxLeft==iCur || aExpr[k].idxRight==iCur ); if( aExpr[k].idxLeft==iCur ) { sqliteExprCode(pParse, aExpr[k].p->pRight); } else { sqliteExprCode(pParse, aExpr[k].p->pLeft); } sqliteVdbeAddOp(v, OP_ForceInt, aExpr[k].p->op==TK_LT || aExpr[k].p->op==TK_GT, brk); sqliteVdbeAddOp(v, OP_MoveTo, iCur, brk); aExpr[k].p = 0; } else { sqliteVdbeAddOp(v, OP_Rewind, iCur, brk); } if( iDirectLt[i]>=0 ) { k = iDirectLt[i]; assert( k<nExpr ); assert( aExpr[k].p!=0 ); assert( aExpr[k].idxLeft==iCur || aExpr[k].idxRight==iCur ); if( aExpr[k].idxLeft==iCur ) { sqliteExprCode(pParse, aExpr[k].p->pRight); } else { sqliteExprCode(pParse, aExpr[k].p->pLeft); } /* sqliteVdbeAddOp(v, OP_MustBeInt, 0, sqliteVdbeCurrentAddr(v)+1); */ pLevel->iMem = pParse->nMem++; sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 1); if( aExpr[k].p->op==TK_LT || aExpr[k].p->op==TK_GT ) { testOp = OP_Ge; } else { testOp = OP_Gt; } aExpr[k].p = 0; } start = sqliteVdbeCurrentAddr(v); pLevel->op = OP_Next; pLevel->p1 = iCur; pLevel->p2 = start; if( testOp!=OP_Noop ) { sqliteVdbeAddOp(v, OP_Recno, iCur, 0); sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0); sqliteVdbeAddOp(v, testOp, 0, brk); } haveKey = 0; } else if( pIdx==0 ) { /* Case 4: There is no usable index. We must do a complete ** scan of the entire database table. */ int start; brk = pLevel->brk = sqliteVdbeMakeLabel(v); cont = pLevel->cont = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Rewind, iCur, brk); start = sqliteVdbeCurrentAddr(v); pLevel->op = OP_Next; pLevel->p1 = iCur; pLevel->p2 = start; haveKey = 0; } else { /* Case 5: The WHERE clause term that refers to the right-most ** column of the index is an inequality. For example, if ** the index is on (x,y,z) and the WHERE clause is of the ** form "x=5 AND y<10" then this case is used. Only the ** right-most column can be an inequality - the rest must ** use the "==" operator. ** ** This case is also used when there are no WHERE clause ** constraints but an index is selected anyway, in order ** to force the output order to conform to an ORDER BY. */ int score = pLevel->score; int nEqColumn = score/8; int start; int leFlag, geFlag; int testOp; /* Evaluate the equality constraints */ for(j=0; j<nEqColumn; j++) { for(k=0; k<nExpr; k++) { if( aExpr[k].p==0 ) continue; if( aExpr[k].idxLeft==iCur && aExpr[k].p->op==TK_EQ && (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight && aExpr[k].p->pLeft->iColumn==pIdx->aiColumn[j] ) { sqliteExprCode(pParse, aExpr[k].p->pRight); aExpr[k].p = 0; break; } if( aExpr[k].idxRight==iCur && aExpr[k].p->op==TK_EQ && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft && aExpr[k].p->pRight->iColumn==pIdx->aiColumn[j] ) { sqliteExprCode(pParse, aExpr[k].p->pLeft); aExpr[k].p = 0; break; } } } /* Duplicate the equality term values because they will all be ** used twice: once to make the termination key and once to make the ** start key. */ for(j=0; j<nEqColumn; j++) { sqliteVdbeAddOp(v, OP_Dup, nEqColumn-1, 0); } /* Labels for the beginning and end of the loop */ cont = pLevel->cont = sqliteVdbeMakeLabel(v); brk = pLevel->brk = sqliteVdbeMakeLabel(v); /* Generate the termination key. This is the key value that ** will end the search. There is no termination key if there ** are no equality terms and no "X<..." term. ** ** 2002-Dec-04: On a reverse-order scan, the so-called "termination" ** key computed here really ends up being the start key. */ if( (score & 1)!=0 ) { for(k=0; k<nExpr; k++) { Expr *pExpr = aExpr[k].p; if( pExpr==0 ) continue; if( aExpr[k].idxLeft==iCur && (pExpr->op==TK_LT || pExpr->op==TK_LE) && (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight && pExpr->pLeft->iColumn==pIdx->aiColumn[j] ) { sqliteExprCode(pParse, pExpr->pRight); leFlag = pExpr->op==TK_LE; aExpr[k].p = 0; break; } if( aExpr[k].idxRight==iCur && (pExpr->op==TK_GT || pExpr->op==TK_GE) && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft && pExpr->pRight->iColumn==pIdx->aiColumn[j] ) { sqliteExprCode(pParse, pExpr->pLeft); leFlag = pExpr->op==TK_GE; aExpr[k].p = 0; break; } } testOp = OP_IdxGE; } else { testOp = nEqColumn>0 ? OP_IdxGE : OP_Noop; leFlag = 1; } if( testOp!=OP_Noop ) { int nCol = nEqColumn + (score & 1); pLevel->iMem = pParse->nMem++; sqliteVdbeAddOp(v, OP_NotNull, -nCol, sqliteVdbeCurrentAddr(v)+3); sqliteVdbeAddOp(v, OP_Pop, nCol, 0); sqliteVdbeAddOp(v, OP_Goto, 0, brk); sqliteVdbeAddOp(v, OP_MakeKey, nCol, 0); sqliteAddIdxKeyType(v, pIdx); if( leFlag ) { sqliteVdbeAddOp(v, OP_IncrKey, 0, 0); } if( pLevel->bRev ) { sqliteVdbeAddOp(v, OP_MoveLt, pLevel->iCur, brk); } else { sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 1); } } else if( pLevel->bRev ) { sqliteVdbeAddOp(v, OP_Last, pLevel->iCur, brk); } /* Generate the start key. This is the key that defines the lower ** bound on the search. There is no start key if there are no ** equality terms and if there is no "X>..." term. In ** that case, generate a "Rewind" instruction in place of the ** start key search. ** ** 2002-Dec-04: In the case of a reverse-order search, the so-called ** "start" key really ends up being used as the termination key. */ if( (score & 2)!=0 ) { for(k=0; k<nExpr; k++) { Expr *pExpr = aExpr[k].p; if( pExpr==0 ) continue; if( aExpr[k].idxLeft==iCur && (pExpr->op==TK_GT || pExpr->op==TK_GE) && (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight && pExpr->pLeft->iColumn==pIdx->aiColumn[j] ) { sqliteExprCode(pParse, pExpr->pRight); geFlag = pExpr->op==TK_GE; aExpr[k].p = 0; break; } if( aExpr[k].idxRight==iCur && (pExpr->op==TK_LT || pExpr->op==TK_LE) && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft && pExpr->pRight->iColumn==pIdx->aiColumn[j] ) { sqliteExprCode(pParse, pExpr->pLeft); geFlag = pExpr->op==TK_LE; aExpr[k].p = 0; break; } } } else { geFlag = 1; } if( nEqColumn>0 || (score&2)!=0 ) { int nCol = nEqColumn + ((score&2)!=0); sqliteVdbeAddOp(v, OP_NotNull, -nCol, sqliteVdbeCurrentAddr(v)+3); sqliteVdbeAddOp(v, OP_Pop, nCol, 0); sqliteVdbeAddOp(v, OP_Goto, 0, brk); sqliteVdbeAddOp(v, OP_MakeKey, nCol, 0); sqliteAddIdxKeyType(v, pIdx); if( !geFlag ) { sqliteVdbeAddOp(v, OP_IncrKey, 0, 0); } if( pLevel->bRev ) { pLevel->iMem = pParse->nMem++; sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 1); testOp = OP_IdxLT; } else { sqliteVdbeAddOp(v, OP_MoveTo, pLevel->iCur, brk); } } else if( pLevel->bRev ) { testOp = OP_Noop; } else { sqliteVdbeAddOp(v, OP_Rewind, pLevel->iCur, brk); } /* Generate the the top of the loop. If there is a termination ** key we have to test for that key and abort at the top of the ** loop. */ start = sqliteVdbeCurrentAddr(v); if( testOp!=OP_Noop ) { sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0); sqliteVdbeAddOp(v, testOp, pLevel->iCur, brk); } sqliteVdbeAddOp(v, OP_RowKey, pLevel->iCur, 0); sqliteVdbeAddOp(v, OP_IdxIsNull, nEqColumn + (score & 1), cont); sqliteVdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0); if( i==pTabList->nSrc-1 && pushKey ) { haveKey = 1; } else { sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0); haveKey = 0; } /* Record the instruction used to terminate the loop. */ pLevel->op = pLevel->bRev ? OP_Prev : OP_Next; pLevel->p1 = pLevel->iCur; pLevel->p2 = start; } loopMask |= getMask(&maskSet, iCur); /* Insert code to test every subexpression that can be completely ** computed using the current set of tables. */ for(j=0; j<nExpr; j++) { if( aExpr[j].p==0 ) continue; if( (aExpr[j].prereqAll & loopMask)!=aExpr[j].prereqAll ) continue; if( pLevel->iLeftJoin && !ExprHasProperty(aExpr[j].p,EP_FromJoin) ) { continue; } if( haveKey ) { haveKey = 0; sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0); } sqliteExprIfFalse(pParse, aExpr[j].p, cont, 1); aExpr[j].p = 0; } brk = cont; /* For a LEFT OUTER JOIN, generate code that will record the fact that ** at least one row of the right table has matched the left table. */ if( pLevel->iLeftJoin ) { pLevel->top = sqliteVdbeCurrentAddr(v); sqliteVdbeAddOp(v, OP_Integer, 1, 0); sqliteVdbeAddOp(v, OP_MemStore, pLevel->iLeftJoin, 1); for(j=0; j<nExpr; j++) { if( aExpr[j].p==0 ) continue; if( (aExpr[j].prereqAll & loopMask)!=aExpr[j].prereqAll ) continue; if( haveKey ) { /* Cannot happen. "haveKey" can only be true if pushKey is true ** an pushKey can only be true for DELETE and UPDATE and there are ** no outer joins with DELETE and UPDATE. */ haveKey = 0; sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0); } sqliteExprIfFalse(pParse, aExpr[j].p, cont, 1); aExpr[j].p = 0; } } } pWInfo->iContinue = cont; if( pushKey && !haveKey ) { sqliteVdbeAddOp(v, OP_Recno, pTabList->a[0].iCursor, 0); } freeMaskSet(&maskSet); return pWInfo; }
/* ** Generate code into the current Vdbe to evaluate the given ** expression and leave the result on the top of stack. */ void sqliteExprCode(Parse *pParse, Expr *pExpr){ Vdbe *v = pParse->pVdbe; int op; if( v==0 || pExpr==0 ) return; switch( pExpr->op ){ case TK_PLUS: op = OP_Add; break; case TK_MINUS: op = OP_Subtract; break; case TK_STAR: op = OP_Multiply; break; case TK_SLASH: op = OP_Divide; break; case TK_AND: op = OP_And; break; case TK_OR: op = OP_Or; break; case TK_LT: op = OP_Lt; break; case TK_LE: op = OP_Le; break; case TK_GT: op = OP_Gt; break; case TK_GE: op = OP_Ge; break; case TK_NE: op = OP_Ne; break; case TK_EQ: op = OP_Eq; break; case TK_ISNULL: op = OP_IsNull; break; case TK_NOTNULL: op = OP_NotNull; break; case TK_NOT: op = OP_Not; break; case TK_UMINUS: op = OP_Negative; break; case TK_BITAND: op = OP_BitAnd; break; case TK_BITOR: op = OP_BitOr; break; case TK_BITNOT: op = OP_BitNot; break; case TK_LSHIFT: op = OP_ShiftLeft; break; case TK_RSHIFT: op = OP_ShiftRight; break; case TK_REM: op = OP_Remainder; break; default: break; } switch( pExpr->op ){ case TK_COLUMN: { if( pParse->useAgg ){ sqliteVdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg); }else if( pExpr->iColumn>=0 ){ sqliteVdbeAddOp(v, OP_Column, pExpr->iTable, pExpr->iColumn); }else{ sqliteVdbeAddOp(v, OP_Recno, pExpr->iTable, 0); } break; } case TK_STRING: case TK_FLOAT: case TK_INTEGER: { if( pExpr->op==TK_INTEGER && sqliteFitsIn32Bits(pExpr->token.z) ){ sqliteVdbeAddOp(v, OP_Integer, atoi(pExpr->token.z), 0); }else{ sqliteVdbeAddOp(v, OP_String, 0, 0); } assert( pExpr->token.z ); sqliteVdbeChangeP3(v, -1, pExpr->token.z, pExpr->token.n); sqliteVdbeDequoteP3(v, -1); break; } case TK_NULL: { sqliteVdbeAddOp(v, OP_String, 0, 0); break; } case TK_VARIABLE: { sqliteVdbeAddOp(v, OP_Variable, pExpr->iTable, 0); break; } case TK_LT: case TK_LE: case TK_GT: case TK_GE: case TK_NE: case TK_EQ: { if( pParse->db->file_format>=4 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){ op += 6; /* Convert numeric opcodes to text opcodes */ } /* Fall through into the next case */ } case TK_AND: case TK_OR: case TK_PLUS: case TK_STAR: case TK_MINUS: case TK_REM: case TK_BITAND: case TK_BITOR: case TK_SLASH: { sqliteExprCode(pParse, pExpr->pLeft); sqliteExprCode(pParse, pExpr->pRight); sqliteVdbeAddOp(v, op, 0, 0); break; } case TK_LSHIFT: case TK_RSHIFT: { sqliteExprCode(pParse, pExpr->pRight); sqliteExprCode(pParse, pExpr->pLeft); sqliteVdbeAddOp(v, op, 0, 0); break; } case TK_CONCAT: { sqliteExprCode(pParse, pExpr->pLeft); sqliteExprCode(pParse, pExpr->pRight); sqliteVdbeAddOp(v, OP_Concat, 2, 0); break; } case TK_UMINUS: { assert( pExpr->pLeft ); if( pExpr->pLeft->op==TK_FLOAT || pExpr->pLeft->op==TK_INTEGER ){ Token *p = &pExpr->pLeft->token; char *z = sqliteMalloc( p->n + 2 ); sprintf(z, "-%.*s", p->n, p->z); if( pExpr->pLeft->op==TK_INTEGER && sqliteFitsIn32Bits(z) ){ sqliteVdbeAddOp(v, OP_Integer, atoi(z), 0); }else{ sqliteVdbeAddOp(v, OP_String, 0, 0); } sqliteVdbeChangeP3(v, -1, z, p->n+1); sqliteFree(z); break; } /* Fall through into TK_NOT */ } case TK_BITNOT: case TK_NOT: { sqliteExprCode(pParse, pExpr->pLeft); sqliteVdbeAddOp(v, op, 0, 0); break; } case TK_ISNULL: case TK_NOTNULL: { int dest; sqliteVdbeAddOp(v, OP_Integer, 1, 0); sqliteExprCode(pParse, pExpr->pLeft); dest = sqliteVdbeCurrentAddr(v) + 2; sqliteVdbeAddOp(v, op, 1, dest); sqliteVdbeAddOp(v, OP_AddImm, -1, 0); break; } case TK_AGG_FUNCTION: { sqliteVdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg); break; } case TK_GLOB: case TK_LIKE: case TK_FUNCTION: { ExprList *pList = pExpr->pList; int nExpr = pList ? pList->nExpr : 0; FuncDef *pDef; int nId; const char *zId; getFunctionName(pExpr, &zId, &nId); pDef = sqliteFindFunction(pParse->db, zId, nId, nExpr, 0); assert( pDef!=0 ); nExpr = sqliteExprCodeExprList(pParse, pList, pDef->includeTypes); sqliteVdbeOp3(v, OP_Function, nExpr, 0, (char*)pDef, P3_POINTER); break; } case TK_SELECT: { sqliteVdbeAddOp(v, OP_MemLoad, pExpr->iColumn, 0); break; } case TK_IN: { int addr; sqliteVdbeAddOp(v, OP_Integer, 1, 0); sqliteExprCode(pParse, pExpr->pLeft); addr = sqliteVdbeCurrentAddr(v); sqliteVdbeAddOp(v, OP_NotNull, -1, addr+4); sqliteVdbeAddOp(v, OP_Pop, 1, 0); sqliteVdbeAddOp(v, OP_String, 0, 0); sqliteVdbeAddOp(v, OP_Goto, 0, addr+6); if( pExpr->pSelect ){ sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, addr+6); }else{ sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, addr+6); } sqliteVdbeAddOp(v, OP_AddImm, -1, 0); break; } case TK_BETWEEN: { sqliteExprCode(pParse, pExpr->pLeft); sqliteVdbeAddOp(v, OP_Dup, 0, 0); sqliteExprCode(pParse, pExpr->pList->a[0].pExpr); sqliteVdbeAddOp(v, OP_Ge, 0, 0); sqliteVdbeAddOp(v, OP_Pull, 1, 0); sqliteExprCode(pParse, pExpr->pList->a[1].pExpr); sqliteVdbeAddOp(v, OP_Le, 0, 0); sqliteVdbeAddOp(v, OP_And, 0, 0); break; } case TK_UPLUS: case TK_AS: { sqliteExprCode(pParse, pExpr->pLeft); break; } case TK_CASE: { int expr_end_label; int jumpInst; int addr; int nExpr; int i; assert(pExpr->pList); assert((pExpr->pList->nExpr % 2) == 0); assert(pExpr->pList->nExpr > 0); nExpr = pExpr->pList->nExpr; expr_end_label = sqliteVdbeMakeLabel(v); if( pExpr->pLeft ){ sqliteExprCode(pParse, pExpr->pLeft); } for(i=0; i<nExpr; i=i+2){ sqliteExprCode(pParse, pExpr->pList->a[i].pExpr); if( pExpr->pLeft ){ sqliteVdbeAddOp(v, OP_Dup, 1, 1); jumpInst = sqliteVdbeAddOp(v, OP_Ne, 1, 0); sqliteVdbeAddOp(v, OP_Pop, 1, 0); }else{ jumpInst = sqliteVdbeAddOp(v, OP_IfNot, 1, 0); } sqliteExprCode(pParse, pExpr->pList->a[i+1].pExpr); sqliteVdbeAddOp(v, OP_Goto, 0, expr_end_label); addr = sqliteVdbeCurrentAddr(v); sqliteVdbeChangeP2(v, jumpInst, addr); } if( pExpr->pLeft ){ sqliteVdbeAddOp(v, OP_Pop, 1, 0); } if( pExpr->pRight ){ sqliteExprCode(pParse, pExpr->pRight); }else{ sqliteVdbeAddOp(v, OP_String, 0, 0); } sqliteVdbeResolveLabel(v, expr_end_label); break; } case TK_RAISE: { if( !pParse->trigStack ){ sqliteErrorMsg(pParse, "RAISE() may only be used within a trigger-program"); pParse->nErr++; return; } if( pExpr->iColumn == OE_Rollback || pExpr->iColumn == OE_Abort || pExpr->iColumn == OE_Fail ){ sqliteVdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn, pExpr->token.z, pExpr->token.n); sqliteVdbeDequoteP3(v, -1); } else { assert( pExpr->iColumn == OE_Ignore ); sqliteVdbeOp3(v, OP_Goto, 0, pParse->trigStack->ignoreJump, "(IGNORE jump)", 0); } } break; } }