xref: /illumos-gate/usr/src/lib/libsqlite/src/delete.c (revision 1da57d55)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate ** 2001 September 15
37c478bd9Sstevel@tonic-gate **
47c478bd9Sstevel@tonic-gate ** The author disclaims copyright to this source code.  In place of
57c478bd9Sstevel@tonic-gate ** a legal notice, here is a blessing:
67c478bd9Sstevel@tonic-gate **
77c478bd9Sstevel@tonic-gate **    May you do good and not evil.
87c478bd9Sstevel@tonic-gate **    May you find forgiveness for yourself and forgive others.
97c478bd9Sstevel@tonic-gate **    May you share freely, never taking more than you give.
107c478bd9Sstevel@tonic-gate **
117c478bd9Sstevel@tonic-gate *************************************************************************
127c478bd9Sstevel@tonic-gate ** This file contains C code routines that are called by the parser
137c478bd9Sstevel@tonic-gate ** to handle DELETE FROM statements.
147c478bd9Sstevel@tonic-gate **
157c478bd9Sstevel@tonic-gate ** $Id: delete.c,v 1.61 2004/02/24 01:05:32 drh Exp $
167c478bd9Sstevel@tonic-gate */
177c478bd9Sstevel@tonic-gate #include "sqliteInt.h"
187c478bd9Sstevel@tonic-gate 
197c478bd9Sstevel@tonic-gate /*
207c478bd9Sstevel@tonic-gate ** Look up every table that is named in pSrc.  If any table is not found,
217c478bd9Sstevel@tonic-gate ** add an error message to pParse->zErrMsg and return NULL.  If all tables
227c478bd9Sstevel@tonic-gate ** are found, return a pointer to the last table.
237c478bd9Sstevel@tonic-gate */
sqliteSrcListLookup(Parse * pParse,SrcList * pSrc)247c478bd9Sstevel@tonic-gate Table *sqliteSrcListLookup(Parse *pParse, SrcList *pSrc){
257c478bd9Sstevel@tonic-gate   Table *pTab = 0;
267c478bd9Sstevel@tonic-gate   int i;
277c478bd9Sstevel@tonic-gate   for(i=0; i<pSrc->nSrc; i++){
287c478bd9Sstevel@tonic-gate     const char *zTab = pSrc->a[i].zName;
297c478bd9Sstevel@tonic-gate     const char *zDb = pSrc->a[i].zDatabase;
307c478bd9Sstevel@tonic-gate     pTab = sqliteLocateTable(pParse, zTab, zDb);
317c478bd9Sstevel@tonic-gate     pSrc->a[i].pTab = pTab;
327c478bd9Sstevel@tonic-gate   }
337c478bd9Sstevel@tonic-gate   return pTab;
347c478bd9Sstevel@tonic-gate }
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate /*
377c478bd9Sstevel@tonic-gate ** Check to make sure the given table is writable.  If it is not
387c478bd9Sstevel@tonic-gate ** writable, generate an error message and return 1.  If it is
397c478bd9Sstevel@tonic-gate ** writable return 0;
407c478bd9Sstevel@tonic-gate */
sqliteIsReadOnly(Parse * pParse,Table * pTab,int viewOk)417c478bd9Sstevel@tonic-gate int sqliteIsReadOnly(Parse *pParse, Table *pTab, int viewOk){
427c478bd9Sstevel@tonic-gate   if( pTab->readOnly ){
437c478bd9Sstevel@tonic-gate     sqliteErrorMsg(pParse, "table %s may not be modified", pTab->zName);
447c478bd9Sstevel@tonic-gate     return 1;
457c478bd9Sstevel@tonic-gate   }
467c478bd9Sstevel@tonic-gate   if( !viewOk && pTab->pSelect ){
477c478bd9Sstevel@tonic-gate     sqliteErrorMsg(pParse, "cannot modify %s because it is a view",pTab->zName);
487c478bd9Sstevel@tonic-gate     return 1;
497c478bd9Sstevel@tonic-gate   }
507c478bd9Sstevel@tonic-gate   return 0;
517c478bd9Sstevel@tonic-gate }
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate /*
547c478bd9Sstevel@tonic-gate ** Process a DELETE FROM statement.
557c478bd9Sstevel@tonic-gate */
sqliteDeleteFrom(Parse * pParse,SrcList * pTabList,Expr * pWhere)567c478bd9Sstevel@tonic-gate void sqliteDeleteFrom(
577c478bd9Sstevel@tonic-gate   Parse *pParse,         /* The parser context */
587c478bd9Sstevel@tonic-gate   SrcList *pTabList,     /* The table from which we should delete things */
597c478bd9Sstevel@tonic-gate   Expr *pWhere           /* The WHERE clause.  May be null */
607c478bd9Sstevel@tonic-gate ){
617c478bd9Sstevel@tonic-gate   Vdbe *v;               /* The virtual database engine */
627c478bd9Sstevel@tonic-gate   Table *pTab;           /* The table from which records will be deleted */
637c478bd9Sstevel@tonic-gate   const char *zDb;       /* Name of database holding pTab */
647c478bd9Sstevel@tonic-gate   int end, addr;         /* A couple addresses of generated code */
657c478bd9Sstevel@tonic-gate   int i;                 /* Loop counter */
667c478bd9Sstevel@tonic-gate   WhereInfo *pWInfo;     /* Information about the WHERE clause */
677c478bd9Sstevel@tonic-gate   Index *pIdx;           /* For looping over indices of the table */
687c478bd9Sstevel@tonic-gate   int iCur;              /* VDBE Cursor number for pTab */
697c478bd9Sstevel@tonic-gate   sqlite *db;            /* Main database structure */
707c478bd9Sstevel@tonic-gate   int isView;            /* True if attempting to delete from a view */
717c478bd9Sstevel@tonic-gate   AuthContext sContext;  /* Authorization context */
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate   int row_triggers_exist = 0;  /* True if any triggers exist */
747c478bd9Sstevel@tonic-gate   int before_triggers;         /* True if there are BEFORE triggers */
757c478bd9Sstevel@tonic-gate   int after_triggers;          /* True if there are AFTER triggers */
767c478bd9Sstevel@tonic-gate   int oldIdx = -1;             /* Cursor for the OLD table of AFTER triggers */
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate   sContext.pParse = 0;
797c478bd9Sstevel@tonic-gate   if( pParse->nErr || sqlite_malloc_failed ){
807c478bd9Sstevel@tonic-gate     pTabList = 0;
817c478bd9Sstevel@tonic-gate     goto delete_from_cleanup;
827c478bd9Sstevel@tonic-gate   }
837c478bd9Sstevel@tonic-gate   db = pParse->db;
847c478bd9Sstevel@tonic-gate   assert( pTabList->nSrc==1 );
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate   /* Locate the table which we want to delete.  This table has to be
877c478bd9Sstevel@tonic-gate   ** put in an SrcList structure because some of the subroutines we
887c478bd9Sstevel@tonic-gate   ** will be calling are designed to work with multiple tables and expect
897c478bd9Sstevel@tonic-gate   ** an SrcList* parameter instead of just a Table* parameter.
907c478bd9Sstevel@tonic-gate   */
917c478bd9Sstevel@tonic-gate   pTab = sqliteSrcListLookup(pParse, pTabList);
927c478bd9Sstevel@tonic-gate   if( pTab==0 )  goto delete_from_cleanup;
93*1da57d55SToomas Soome   before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
947c478bd9Sstevel@tonic-gate                          TK_DELETE, TK_BEFORE, TK_ROW, 0);
95*1da57d55SToomas Soome   after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
967c478bd9Sstevel@tonic-gate                          TK_DELETE, TK_AFTER, TK_ROW, 0);
977c478bd9Sstevel@tonic-gate   row_triggers_exist = before_triggers || after_triggers;
987c478bd9Sstevel@tonic-gate   isView = pTab->pSelect!=0;
997c478bd9Sstevel@tonic-gate   if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
1007c478bd9Sstevel@tonic-gate     goto delete_from_cleanup;
1017c478bd9Sstevel@tonic-gate   }
1027c478bd9Sstevel@tonic-gate   assert( pTab->iDb<db->nDb );
1037c478bd9Sstevel@tonic-gate   zDb = db->aDb[pTab->iDb].zName;
1047c478bd9Sstevel@tonic-gate   if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
1057c478bd9Sstevel@tonic-gate     goto delete_from_cleanup;
1067c478bd9Sstevel@tonic-gate   }
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate   /* If pTab is really a view, make sure it has been initialized.
1097c478bd9Sstevel@tonic-gate   */
1107c478bd9Sstevel@tonic-gate   if( isView && sqliteViewGetColumnNames(pParse, pTab) ){
1117c478bd9Sstevel@tonic-gate     goto delete_from_cleanup;
1127c478bd9Sstevel@tonic-gate   }
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate   /* Allocate a cursor used to store the old.* data for a trigger.
1157c478bd9Sstevel@tonic-gate   */
116*1da57d55SToomas Soome   if( row_triggers_exist ){
1177c478bd9Sstevel@tonic-gate     oldIdx = pParse->nTab++;
1187c478bd9Sstevel@tonic-gate   }
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate   /* Resolve the column names in all the expressions.
1217c478bd9Sstevel@tonic-gate   */
1227c478bd9Sstevel@tonic-gate   assert( pTabList->nSrc==1 );
1237c478bd9Sstevel@tonic-gate   iCur = pTabList->a[0].iCursor = pParse->nTab++;
1247c478bd9Sstevel@tonic-gate   if( pWhere ){
1257c478bd9Sstevel@tonic-gate     if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){
1267c478bd9Sstevel@tonic-gate       goto delete_from_cleanup;
1277c478bd9Sstevel@tonic-gate     }
1287c478bd9Sstevel@tonic-gate     if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
1297c478bd9Sstevel@tonic-gate       goto delete_from_cleanup;
1307c478bd9Sstevel@tonic-gate     }
1317c478bd9Sstevel@tonic-gate   }
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate   /* Start the view context
1347c478bd9Sstevel@tonic-gate   */
1357c478bd9Sstevel@tonic-gate   if( isView ){
1367c478bd9Sstevel@tonic-gate     sqliteAuthContextPush(pParse, &sContext, pTab->zName);
1377c478bd9Sstevel@tonic-gate   }
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate   /* Begin generating code.
1407c478bd9Sstevel@tonic-gate   */
1417c478bd9Sstevel@tonic-gate   v = sqliteGetVdbe(pParse);
1427c478bd9Sstevel@tonic-gate   if( v==0 ){
1437c478bd9Sstevel@tonic-gate     goto delete_from_cleanup;
1447c478bd9Sstevel@tonic-gate   }
1457c478bd9Sstevel@tonic-gate   sqliteBeginWriteOperation(pParse, row_triggers_exist, pTab->iDb);
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate   /* If we are trying to delete from a view, construct that view into
1487c478bd9Sstevel@tonic-gate   ** a temporary table.
1497c478bd9Sstevel@tonic-gate   */
1507c478bd9Sstevel@tonic-gate   if( isView ){
1517c478bd9Sstevel@tonic-gate     Select *pView = sqliteSelectDup(pTab->pSelect);
1527c478bd9Sstevel@tonic-gate     sqliteSelect(pParse, pView, SRT_TempTable, iCur, 0, 0, 0);
1537c478bd9Sstevel@tonic-gate     sqliteSelectDelete(pView);
1547c478bd9Sstevel@tonic-gate   }
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate   /* Initialize the counter of the number of rows deleted, if
1577c478bd9Sstevel@tonic-gate   ** we are counting rows.
1587c478bd9Sstevel@tonic-gate   */
1597c478bd9Sstevel@tonic-gate   if( db->flags & SQLITE_CountRows ){
1607c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_Integer, 0, 0);
1617c478bd9Sstevel@tonic-gate   }
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate   /* Special case: A DELETE without a WHERE clause deletes everything.
1647c478bd9Sstevel@tonic-gate   ** It is easier just to erase the whole table.  Note, however, that
1657c478bd9Sstevel@tonic-gate   ** this means that the row change count will be incorrect.
1667c478bd9Sstevel@tonic-gate   */
1677c478bd9Sstevel@tonic-gate   if( pWhere==0 && !row_triggers_exist ){
1687c478bd9Sstevel@tonic-gate     if( db->flags & SQLITE_CountRows ){
1697c478bd9Sstevel@tonic-gate       /* If counting rows deleted, just count the total number of
1707c478bd9Sstevel@tonic-gate       ** entries in the table. */
1717c478bd9Sstevel@tonic-gate       int endOfLoop = sqliteVdbeMakeLabel(v);
1727c478bd9Sstevel@tonic-gate       int addr;
1737c478bd9Sstevel@tonic-gate       if( !isView ){
1747c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
1757c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
1767c478bd9Sstevel@tonic-gate       }
1777c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_Rewind, iCur, sqliteVdbeCurrentAddr(v)+2);
1787c478bd9Sstevel@tonic-gate       addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
1797c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_Next, iCur, addr);
1807c478bd9Sstevel@tonic-gate       sqliteVdbeResolveLabel(v, endOfLoop);
1817c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_Close, iCur, 0);
1827c478bd9Sstevel@tonic-gate     }
1837c478bd9Sstevel@tonic-gate     if( !isView ){
1847c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb);
1857c478bd9Sstevel@tonic-gate       for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
1867c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb);
1877c478bd9Sstevel@tonic-gate       }
1887c478bd9Sstevel@tonic-gate     }
1897c478bd9Sstevel@tonic-gate   }
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate   /* The usual case: There is a WHERE clause so we have to scan through
1927c478bd9Sstevel@tonic-gate   ** the table and pick which records to delete.
1937c478bd9Sstevel@tonic-gate   */
1947c478bd9Sstevel@tonic-gate   else{
1957c478bd9Sstevel@tonic-gate     /* Begin the database scan
1967c478bd9Sstevel@tonic-gate     */
1977c478bd9Sstevel@tonic-gate     pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1, 0);
1987c478bd9Sstevel@tonic-gate     if( pWInfo==0 ) goto delete_from_cleanup;
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate     /* Remember the key of every item to be deleted.
2017c478bd9Sstevel@tonic-gate     */
2027c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_ListWrite, 0, 0);
2037c478bd9Sstevel@tonic-gate     if( db->flags & SQLITE_CountRows ){
2047c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
2057c478bd9Sstevel@tonic-gate     }
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate     /* End the database scan loop.
2087c478bd9Sstevel@tonic-gate     */
2097c478bd9Sstevel@tonic-gate     sqliteWhereEnd(pWInfo);
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate     /* Open the pseudo-table used to store OLD if there are triggers.
2127c478bd9Sstevel@tonic-gate     */
2137c478bd9Sstevel@tonic-gate     if( row_triggers_exist ){
2147c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
2157c478bd9Sstevel@tonic-gate     }
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate     /* Delete every item whose key was written to the list during the
2187c478bd9Sstevel@tonic-gate     ** database scan.  We have to delete items after the scan is complete
2197c478bd9Sstevel@tonic-gate     ** because deleting an item can change the scan order.
2207c478bd9Sstevel@tonic-gate     */
2217c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
2227c478bd9Sstevel@tonic-gate     end = sqliteVdbeMakeLabel(v);
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate     /* This is the beginning of the delete loop when there are
2257c478bd9Sstevel@tonic-gate     ** row triggers.
2267c478bd9Sstevel@tonic-gate     */
2277c478bd9Sstevel@tonic-gate     if( row_triggers_exist ){
2287c478bd9Sstevel@tonic-gate       addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
2297c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_Dup, 0, 0);
2307c478bd9Sstevel@tonic-gate       if( !isView ){
2317c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
2327c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
2337c478bd9Sstevel@tonic-gate       }
2347c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0);
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_Recno, iCur, 0);
2377c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_RowData, iCur, 0);
2387c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
2397c478bd9Sstevel@tonic-gate       if( !isView ){
2407c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(v, OP_Close, iCur, 0);
2417c478bd9Sstevel@tonic-gate       }
2427c478bd9Sstevel@tonic-gate 
243*1da57d55SToomas Soome       sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
2447c478bd9Sstevel@tonic-gate           oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
2457c478bd9Sstevel@tonic-gate 	  addr);
2467c478bd9Sstevel@tonic-gate     }
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate     if( !isView ){
2497c478bd9Sstevel@tonic-gate       /* Open cursors for the table we are deleting from and all its
2507c478bd9Sstevel@tonic-gate       ** indices.  If there are row triggers, this happens inside the
2517c478bd9Sstevel@tonic-gate       ** OP_ListRead loop because the cursor have to all be closed
2527c478bd9Sstevel@tonic-gate       ** before the trigger fires.  If there are no row triggers, the
2537c478bd9Sstevel@tonic-gate       ** cursors are opened only once on the outside the loop.
2547c478bd9Sstevel@tonic-gate       */
2557c478bd9Sstevel@tonic-gate       pParse->nTab = iCur + 1;
2567c478bd9Sstevel@tonic-gate       sqliteOpenTableAndIndices(pParse, pTab, iCur);
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate       /* This is the beginning of the delete loop when there are no
2597c478bd9Sstevel@tonic-gate       ** row triggers */
260*1da57d55SToomas Soome       if( !row_triggers_exist ){
2617c478bd9Sstevel@tonic-gate         addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
2627c478bd9Sstevel@tonic-gate       }
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate       /* Delete the row */
2657c478bd9Sstevel@tonic-gate       sqliteGenerateRowDelete(db, v, pTab, iCur, pParse->trigStack==0);
2667c478bd9Sstevel@tonic-gate     }
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate     /* If there are row triggers, close all cursors then invoke
2697c478bd9Sstevel@tonic-gate     ** the AFTER triggers
2707c478bd9Sstevel@tonic-gate     */
2717c478bd9Sstevel@tonic-gate     if( row_triggers_exist ){
2727c478bd9Sstevel@tonic-gate       if( !isView ){
2737c478bd9Sstevel@tonic-gate         for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
2747c478bd9Sstevel@tonic-gate           sqliteVdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
2757c478bd9Sstevel@tonic-gate         }
2767c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(v, OP_Close, iCur, 0);
2777c478bd9Sstevel@tonic-gate       }
278*1da57d55SToomas Soome       sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1,
2797c478bd9Sstevel@tonic-gate           oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
2807c478bd9Sstevel@tonic-gate 	  addr);
2817c478bd9Sstevel@tonic-gate     }
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate     /* End of the delete loop */
2847c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_Goto, 0, addr);
2857c478bd9Sstevel@tonic-gate     sqliteVdbeResolveLabel(v, end);
2867c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_ListReset, 0, 0);
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate     /* Close the cursors after the loop if there are no row triggers */
2897c478bd9Sstevel@tonic-gate     if( !row_triggers_exist ){
2907c478bd9Sstevel@tonic-gate       for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
2917c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
2927c478bd9Sstevel@tonic-gate       }
2937c478bd9Sstevel@tonic-gate       sqliteVdbeAddOp(v, OP_Close, iCur, 0);
2947c478bd9Sstevel@tonic-gate       pParse->nTab = iCur;
2957c478bd9Sstevel@tonic-gate     }
2967c478bd9Sstevel@tonic-gate   }
2977c478bd9Sstevel@tonic-gate   sqliteVdbeAddOp(v, OP_SetCounts, 0, 0);
2987c478bd9Sstevel@tonic-gate   sqliteEndWriteOperation(pParse);
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate   /*
3017c478bd9Sstevel@tonic-gate   ** Return the number of rows that were deleted.
3027c478bd9Sstevel@tonic-gate   */
3037c478bd9Sstevel@tonic-gate   if( db->flags & SQLITE_CountRows ){
3047c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_ColumnName, 0, 1);
3057c478bd9Sstevel@tonic-gate     sqliteVdbeChangeP3(v, -1, "rows deleted", P3_STATIC);
3067c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_Callback, 1, 0);
3077c478bd9Sstevel@tonic-gate   }
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate delete_from_cleanup:
3107c478bd9Sstevel@tonic-gate   sqliteAuthContextPop(&sContext);
3117c478bd9Sstevel@tonic-gate   sqliteSrcListDelete(pTabList);
3127c478bd9Sstevel@tonic-gate   sqliteExprDelete(pWhere);
3137c478bd9Sstevel@tonic-gate   return;
3147c478bd9Sstevel@tonic-gate }
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate /*
3177c478bd9Sstevel@tonic-gate ** This routine generates VDBE code that causes a single row of a
3187c478bd9Sstevel@tonic-gate ** single table to be deleted.
3197c478bd9Sstevel@tonic-gate **
3207c478bd9Sstevel@tonic-gate ** The VDBE must be in a particular state when this routine is called.
3217c478bd9Sstevel@tonic-gate ** These are the requirements:
3227c478bd9Sstevel@tonic-gate **
3237c478bd9Sstevel@tonic-gate **   1.  A read/write cursor pointing to pTab, the table containing the row
3247c478bd9Sstevel@tonic-gate **       to be deleted, must be opened as cursor number "base".
3257c478bd9Sstevel@tonic-gate **
3267c478bd9Sstevel@tonic-gate **   2.  Read/write cursors for all indices of pTab must be open as
3277c478bd9Sstevel@tonic-gate **       cursor number base+i for the i-th index.
3287c478bd9Sstevel@tonic-gate **
3297c478bd9Sstevel@tonic-gate **   3.  The record number of the row to be deleted must be on the top
3307c478bd9Sstevel@tonic-gate **       of the stack.
3317c478bd9Sstevel@tonic-gate **
3327c478bd9Sstevel@tonic-gate ** This routine pops the top of the stack to remove the record number
3337c478bd9Sstevel@tonic-gate ** and then generates code to remove both the table record and all index
3347c478bd9Sstevel@tonic-gate ** entries that point to that record.
3357c478bd9Sstevel@tonic-gate */
sqliteGenerateRowDelete(sqlite * db,Vdbe * v,Table * pTab,int iCur,int count)3367c478bd9Sstevel@tonic-gate void sqliteGenerateRowDelete(
3377c478bd9Sstevel@tonic-gate   sqlite *db,        /* The database containing the index */
3387c478bd9Sstevel@tonic-gate   Vdbe *v,           /* Generate code into this VDBE */
3397c478bd9Sstevel@tonic-gate   Table *pTab,       /* Table containing the row to be deleted */
3407c478bd9Sstevel@tonic-gate   int iCur,          /* Cursor number for the table */
3417c478bd9Sstevel@tonic-gate   int count          /* Increment the row change counter */
3427c478bd9Sstevel@tonic-gate ){
3437c478bd9Sstevel@tonic-gate   int addr;
3447c478bd9Sstevel@tonic-gate   addr = sqliteVdbeAddOp(v, OP_NotExists, iCur, 0);
3457c478bd9Sstevel@tonic-gate   sqliteGenerateRowIndexDelete(db, v, pTab, iCur, 0);
3467c478bd9Sstevel@tonic-gate   sqliteVdbeAddOp(v, OP_Delete, iCur,
3477c478bd9Sstevel@tonic-gate     (count?OPFLAG_NCHANGE:0) | OPFLAG_CSCHANGE);
3487c478bd9Sstevel@tonic-gate   sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v));
3497c478bd9Sstevel@tonic-gate }
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate /*
3527c478bd9Sstevel@tonic-gate ** This routine generates VDBE code that causes the deletion of all
3537c478bd9Sstevel@tonic-gate ** index entries associated with a single row of a single table.
3547c478bd9Sstevel@tonic-gate **
3557c478bd9Sstevel@tonic-gate ** The VDBE must be in a particular state when this routine is called.
3567c478bd9Sstevel@tonic-gate ** These are the requirements:
3577c478bd9Sstevel@tonic-gate **
3587c478bd9Sstevel@tonic-gate **   1.  A read/write cursor pointing to pTab, the table containing the row
3597c478bd9Sstevel@tonic-gate **       to be deleted, must be opened as cursor number "iCur".
3607c478bd9Sstevel@tonic-gate **
3617c478bd9Sstevel@tonic-gate **   2.  Read/write cursors for all indices of pTab must be open as
3627c478bd9Sstevel@tonic-gate **       cursor number iCur+i for the i-th index.
3637c478bd9Sstevel@tonic-gate **
3647c478bd9Sstevel@tonic-gate **   3.  The "iCur" cursor must be pointing to the row that is to be
3657c478bd9Sstevel@tonic-gate **       deleted.
3667c478bd9Sstevel@tonic-gate */
sqliteGenerateRowIndexDelete(sqlite * db,Vdbe * v,Table * pTab,int iCur,char * aIdxUsed)3677c478bd9Sstevel@tonic-gate void sqliteGenerateRowIndexDelete(
3687c478bd9Sstevel@tonic-gate   sqlite *db,        /* The database containing the index */
3697c478bd9Sstevel@tonic-gate   Vdbe *v,           /* Generate code into this VDBE */
3707c478bd9Sstevel@tonic-gate   Table *pTab,       /* Table containing the row to be deleted */
3717c478bd9Sstevel@tonic-gate   int iCur,          /* Cursor number for the table */
3727c478bd9Sstevel@tonic-gate   char *aIdxUsed     /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */
3737c478bd9Sstevel@tonic-gate ){
3747c478bd9Sstevel@tonic-gate   int i;
3757c478bd9Sstevel@tonic-gate   Index *pIdx;
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate   for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
3787c478bd9Sstevel@tonic-gate     int j;
3797c478bd9Sstevel@tonic-gate     if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue;
3807c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_Recno, iCur, 0);
3817c478bd9Sstevel@tonic-gate     for(j=0; j<pIdx->nColumn; j++){
3827c478bd9Sstevel@tonic-gate       int idx = pIdx->aiColumn[j];
3837c478bd9Sstevel@tonic-gate       if( idx==pTab->iPKey ){
3847c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(v, OP_Dup, j, 0);
3857c478bd9Sstevel@tonic-gate       }else{
3867c478bd9Sstevel@tonic-gate         sqliteVdbeAddOp(v, OP_Column, iCur, idx);
3877c478bd9Sstevel@tonic-gate       }
3887c478bd9Sstevel@tonic-gate     }
3897c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
3907c478bd9Sstevel@tonic-gate     if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx);
3917c478bd9Sstevel@tonic-gate     sqliteVdbeAddOp(v, OP_IdxDelete, iCur+i, 0);
3927c478bd9Sstevel@tonic-gate   }
3937c478bd9Sstevel@tonic-gate }
394