123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- /*
- ** 2001 September 15
- **
- ** The author disclaims copyright to this source code. In place of
- ** a legal notice, here is a blessing:
- **
- ** May you do good and not evil.
- ** May you find forgiveness for yourself and forgive others.
- ** May you share freely, never taking more than you give.
- **
- *************************************************************************
- ** This file contains C code routines that are called by the parser
- ** in order to generate code for DELETE FROM statements.
- **
- ** $Id: delete.c,v 1.134 2007/12/12 17:42:53 danielk1977 Exp $
- */
- #include "sqliteInt.h"
- /*
- ** Look up every table that is named in pSrc. If any table is not found,
- ** add an error message to pParse->zErrMsg and return NULL. If all tables
- ** are found, return a pointer to the last table.
- */
- Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
- Table *pTab = 0;
- int i;
- struct SrcList_item *pItem;
- for(i=0, pItem=pSrc->a; i<pSrc->nSrc; i++, pItem++){
- pTab = sqlite3LocateTable(pParse, pItem->zName, pItem->zDatabase);
- sqlite3DeleteTable(pItem->pTab);
- pItem->pTab = pTab;
- if( pTab ){
- pTab->nRef++;
- }
- }
- return pTab;
- }
- /*
- ** Check to make sure the given table is writable. If it is not
- ** writable, generate an error message and return 1. If it is
- ** writable return 0;
- */
- int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
- if( (pTab->readOnly && (pParse->db->flags & SQLITE_WriteSchema)==0
- && pParse->nested==0)
- #ifndef SQLITE_OMIT_VIRTUALTABLE
- || (pTab->pMod && pTab->pMod->pModule->xUpdate==0)
- #endif
- ){
- sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName);
- return 1;
- }
- #ifndef SQLITE_OMIT_VIEW
- if( !viewOk && pTab->pSelect ){
- sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName);
- return 1;
- }
- #endif
- return 0;
- }
- /*
- ** Generate code that will open a table for reading.
- */
- void sqlite3OpenTable(
- Parse *p, /* Generate code into this VDBE */
- int iCur, /* The cursor number of the table */
- int iDb, /* The database index in sqlite3.aDb[] */
- Table *pTab, /* The table to be opened */
- int opcode /* OP_OpenRead or OP_OpenWrite */
- ){
- Vdbe *v;
- if( IsVirtual(pTab) ) return;
- v = sqlite3GetVdbe(p);
- assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
- sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite), pTab->zName);
- sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
- VdbeComment((v, "# %s", pTab->zName));
- sqlite3VdbeAddOp(v, opcode, iCur, pTab->tnum);
- sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
- }
- /*
- ** Generate code for a DELETE FROM statement.
- **
- ** DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL;
- ** \________/ \________________/
- ** pTabList pWhere
- */
- void sqlite3DeleteFrom(
- 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 = 0; /* 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 */
- sqlite3 *db; /* Main database structure */
- AuthContext sContext; /* Authorization context */
- int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */
- NameContext sNC; /* Name context to resolve expressions in */
- int iDb; /* Database number */
- int memCnt = 0; /* Memory cell used for change counting */
- #ifndef SQLITE_OMIT_TRIGGER
- int isView; /* True if attempting to delete from a view */
- int triggers_exist = 0; /* True if any triggers exist */
- #endif
- sContext.pParse = 0;
- db = pParse->db;
- if( pParse->nErr || db->mallocFailed ){
- goto delete_from_cleanup;
- }
- 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 = sqlite3SrcListLookup(pParse, pTabList);
- if( pTab==0 ) goto delete_from_cleanup;
- /* Figure out if we have any triggers and if the table being
- ** deleted from is a view
- */
- #ifndef SQLITE_OMIT_TRIGGER
- triggers_exist = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0);
- isView = pTab->pSelect!=0;
- #else
- # define triggers_exist 0
- # define isView 0
- #endif
- #ifdef SQLITE_OMIT_VIEW
- # undef isView
- # define isView 0
- #endif
- if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){
- goto delete_from_cleanup;
- }
- iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
- assert( iDb<db->nDb );
- zDb = db->aDb[iDb].zName;
- if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
- goto delete_from_cleanup;
- }
- /* If pTab is really a view, make sure it has been initialized.
- */
- if( sqlite3ViewGetColumnNames(pParse, pTab) ){
- goto delete_from_cleanup;
- }
- /* Allocate a cursor used to store the old.* data for a trigger.
- */
- if( triggers_exist ){
- oldIdx = pParse->nTab++;
- }
- /* Resolve the column names in the WHERE clause.
- */
- assert( pTabList->nSrc==1 );
- iCur = pTabList->a[0].iCursor = pParse->nTab++;
- memset(&sNC, 0, sizeof(sNC));
- sNC.pParse = pParse;
- sNC.pSrcList = pTabList;
- if( sqlite3ExprResolveNames(&sNC, pWhere) ){
- goto delete_from_cleanup;
- }
- /* Start the view context
- */
- if( isView ){
- sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
- }
- /* Begin generating code.
- */
- v = sqlite3GetVdbe(pParse);
- if( v==0 ){
- goto delete_from_cleanup;
- }
- if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
- sqlite3BeginWriteOperation(pParse, triggers_exist, iDb);
- /* If we are trying to delete from a view, realize that view into
- ** a ephemeral table.
- */
- if( isView ){
- Select *pView = sqlite3SelectDup(db, pTab->pSelect);
- sqlite3Select(pParse, pView, SRT_EphemTab, iCur, 0, 0, 0, 0);
- sqlite3SelectDelete(pView);
- }
- /* Initialize the counter of the number of rows deleted, if
- ** we are counting rows.
- */
- if( db->flags & SQLITE_CountRows ){
- memCnt = pParse->nMem++;
- sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt);
- }
- /* 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 && !triggers_exist && !IsVirtual(pTab) ){
- if( db->flags & SQLITE_CountRows ){
- /* If counting rows deleted, just count the total number of
- ** entries in the table. */
- int addr2;
- if( !isView ){
- sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
- }
- sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2);
- addr2 = sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
- sqlite3VdbeAddOp(v, OP_Next, iCur, addr2);
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
- }
- if( !isView ){
- sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, iDb);
- if( !pParse->nested ){
- sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
- }
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- assert( pIdx->pSchema==pTab->pSchema );
- sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, 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 = sqlite3WhereBegin(pParse, pTabList, pWhere, 0);
- if( pWInfo==0 ) goto delete_from_cleanup;
- /* Remember the rowid of every item to be deleted.
- */
- sqlite3VdbeAddOp(v, IsVirtual(pTab) ? OP_VRowid : OP_Rowid, iCur, 0);
- sqlite3VdbeAddOp(v, OP_FifoWrite, 0, 0);
- if( db->flags & SQLITE_CountRows ){
- sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
- }
- /* End the database scan loop.
- */
- sqlite3WhereEnd(pWInfo);
- /* Open the pseudo-table used to store OLD if there are triggers.
- */
- if( triggers_exist ){
- sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
- sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
- }
- /* 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.
- */
- end = sqlite3VdbeMakeLabel(v);
- /* This is the beginning of the delete loop when there are
- ** row triggers.
- */
- if( triggers_exist ){
- int mem1 = pParse->nMem++;
- addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
- sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);
- sqlite3VdbeAddOp(v, OP_MemStore, mem1, 0);
- if( !isView ){
- sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
- }
- sqlite3VdbeAddOp(v, OP_NotExists, iCur, addr);
- sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
- sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
- sqlite3VdbeAddOp(v, OP_Insert, oldIdx, 0);
- if( !isView ){
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
- }
- (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab,
- -1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
- addr);
- if( !isView ){
- sqlite3VdbeAddOp(v, OP_MemLoad, mem1, 0);
- }
- }
- 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_FifoRead 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.
- */
- sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
- /* This is the beginning of the delete loop when there are no
- ** row triggers */
- if( !triggers_exist ){
- addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
- sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);
- }
- /* Delete the row */
- #ifndef SQLITE_OMIT_VIRTUALTABLE
- if( IsVirtual(pTab) ){
- pParse->pVirtualLock = pTab;
- sqlite3VdbeOp3(v, OP_VUpdate, 0, 1, (const char*)pTab->pVtab, P3_VTAB);
- }else
- #endif
- {
- sqlite3GenerateRowDelete(db, v, pTab, iCur, pParse->nested==0);
- }
- }
- /* If there are row triggers, close all cursors then invoke
- ** the AFTER triggers
- */
- if( triggers_exist ){
- if( !isView ){
- for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
- sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
- }
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
- }
- (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1,
- oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
- addr);
- }
- /* End of the delete loop */
- sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
- sqlite3VdbeResolveLabel(v, end);
- /* Close the cursors after the loop if there are no row triggers */
- if( !triggers_exist && !IsVirtual(pTab) ){
- for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
- sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
- }
- sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
- }
- }
- /*
- ** Return the number of rows that were deleted. If this routine is
- ** generating code because of a call to sqlite3NestedParse(), do not
- ** invoke the callback function.
- */
- if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){
- sqlite3VdbeAddOp(v, OP_MemLoad, memCnt, 0);
- sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", P3_STATIC);
- }
- delete_from_cleanup:
- sqlite3AuthContextPop(&sContext);
- sqlite3SrcListDelete(pTabList);
- sqlite3ExprDelete(pWhere);
- return;
- }
- /*
- ** This routine generates VDBE code that causes a single row of a
- ** single table to be deleted.
- **
- ** The VDBE must be in a particular state when this routine is called.
- ** These are the requirements:
- **
- ** 1. A read/write cursor pointing to pTab, the table containing the row
- ** to be deleted, must be opened as cursor number "base".
- **
- ** 2. Read/write cursors for all indices of pTab must be open as
- ** cursor number base+i for the i-th index.
- **
- ** 3. The record number of the row to be deleted must be on the top
- ** of the stack.
- **
- ** This routine pops the top of the stack to remove the record number
- ** and then generates code to remove both the table record and all index
- ** entries that point to that record.
- */
- void sqlite3GenerateRowDelete(
- sqlite3 *db, /* The database containing the index */
- Vdbe *v, /* Generate code into this VDBE */
- Table *pTab, /* Table containing the row to be deleted */
- int iCur, /* Cursor number for the table */
- int count /* Increment the row change counter */
- ){
- int addr;
- addr = sqlite3VdbeAddOp(v, OP_NotExists, iCur, 0);
- sqlite3GenerateRowIndexDelete(v, pTab, iCur, 0);
- sqlite3VdbeAddOp(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
- if( count ){
- sqlite3VdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
- }
- sqlite3VdbeJumpHere(v, addr);
- }
- /*
- ** This routine generates VDBE code that causes the deletion of all
- ** index entries associated with a single row of a single table.
- **
- ** The VDBE must be in a particular state when this routine is called.
- ** These are the requirements:
- **
- ** 1. A read/write cursor pointing to pTab, the table containing the row
- ** to be deleted, must be opened as cursor number "iCur".
- **
- ** 2. Read/write cursors for all indices of pTab must be open as
- ** cursor number iCur+i for the i-th index.
- **
- ** 3. The "iCur" cursor must be pointing to the row that is to be
- ** deleted.
- */
- void sqlite3GenerateRowIndexDelete(
- Vdbe *v, /* Generate code into this VDBE */
- Table *pTab, /* Table containing the row to be deleted */
- int iCur, /* Cursor number for the table */
- char *aIdxUsed /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */
- ){
- int i;
- Index *pIdx;
- for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
- if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue;
- sqlite3GenerateIndexKey(v, pIdx, iCur);
- sqlite3VdbeAddOp(v, OP_IdxDelete, iCur+i, 0);
- }
- }
- /*
- ** Generate code that will assemble an index key and put it on the top
- ** of the tack. The key with be for index pIdx which is an index on pTab.
- ** iCur is the index of a cursor open on the pTab table and pointing to
- ** the entry that needs indexing.
- */
- void sqlite3GenerateIndexKey(
- Vdbe *v, /* Generate code into this VDBE */
- Index *pIdx, /* The index for which to generate a key */
- int iCur /* Cursor number for the pIdx->pTable table */
- ){
- int j;
- Table *pTab = pIdx->pTable;
- sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
- for(j=0; j<pIdx->nColumn; j++){
- int idx = pIdx->aiColumn[j];
- if( idx==pTab->iPKey ){
- sqlite3VdbeAddOp(v, OP_Dup, j, 0);
- }else{
- sqlite3VdbeAddOp(v, OP_Column, iCur, idx);
- sqlite3ColumnDefault(v, pTab, idx);
- }
- }
- sqlite3VdbeAddOp(v, OP_MakeIdxRec, pIdx->nColumn, 0);
- sqlite3IndexAffinityStr(v, pIdx);
- }
|