/*** 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.*/#include "sqliteInt.h"/*** While a SrcList can in general represent multiple tables and subqueries** (as in the FROM clause of a SELECT statement) in this case it contains** the name of a single table, as one might find in an INSERT, DELETE,** or UPDATE statement. Look up that table in the symbol table and** return a pointer. Set an error message and return NULL if the table** name is not found or if any other error occurs.**** The following fields are initialized appropriate in pSrc:**** pSrc->a[0].spTab Pointer to the Table object** pSrc->a[0].u2.pIBIndex Pointer to the INDEXED BY index, if there is one***/Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){SrcItem *pItem = pSrc->a;Table *pTab;assert( pItem && pSrc->nSrc>=1 );pTab = sqlite3LocateTableItem(pParse, 0, pItem);if( pItem->pSTab ) sqlite3DeleteTable(pParse->db, pItem->pSTab);pItem->pSTab = pTab;pItem->fg.notCte = 1;if( pTab ){pTab->nTabRef++;if( pItem->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pItem) ){pTab = 0;}}return pTab;}/* Generate byte-code that will report the number of rows modified** by a DELETE, INSERT, or UPDATE statement.*/void sqlite3CodeChangeCount(Vdbe *v, int regCounter, const char *zColName){sqlite3VdbeAddOp0(v, OP_FkCheck);sqlite3VdbeAddOp2(v, OP_ResultRow, regCounter, 1);sqlite3VdbeSetNumCols(v, 1);sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zColName, SQLITE_STATIC);}/* Return true if table pTab is read-only.**** A table is read-only if any of the following are true:**** 1) It is a virtual table and no implementation of the xUpdate method** has been provided**** 2) A trigger is currently being coded and the table is a virtual table** that is SQLITE_VTAB_DIRECTONLY or if PRAGMA trusted_schema=OFF and** the table is not SQLITE_VTAB_INNOCUOUS.**** 3) It is a system table (i.e. sqlite_schema), this call is not** part of a nested parse and writable_schema pragma has not** been specified**** 4) The table is a shadow table, the database connection is in** defensive mode, and the current sqlite3_prepare()** is for a top-level SQL statement.*/static int vtabIsReadOnly(Parse *pParse, Table *pTab){assert( IsVirtual(pTab) );if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){return 1;}/* Within triggers:** * Do not allow DELETE, INSERT, or UPDATE of SQLITE_VTAB_DIRECTONLY** virtual tables** * Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS** virtual tables if PRAGMA trusted_schema=ON.*/if( (pParse->pToplevel!=0 || (pParse->prepFlags & SQLITE_PREPARE_FROM_DDL))&& pTab->u.vtab.p->eVtabRisk >((pParse->db->flags & SQLITE_TrustedSchema)!=0)){sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"",pTab->zName);}return 0;}static int tabIsReadOnly(Parse *pParse, Table *pTab){sqlite3 *db;if( IsVirtual(pTab) ){return vtabIsReadOnly(pParse, pTab);}if( (pTab->tabFlags & (TF_Readonly|TF_Shadow))==0 ) return 0;db = pParse->db;if( (pTab->tabFlags & TF_Readonly)!=0 ){return sqlite3WritableSchema(db)==0 && pParse->nested==0;}assert( pTab->tabFlags & TF_Shadow );return sqlite3ReadOnlyShadowTables(db);}/*** Check to make sure the given table is writable.**** If pTab is not writable -> generate an error message and return 1.** If pTab is writable but other errors have occurred -> return 1.** If pTab is writable and no prior errors -> return 0;*/int sqlite3IsReadOnly(Parse *pParse, Table *pTab, Trigger *pTrigger){if( tabIsReadOnly(pParse, pTab) ){sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName);return 1;}#ifndef SQLITE_OMIT_VIEWif( IsView(pTab)&& (pTrigger==0 || (pTrigger->bReturning && pTrigger->pNext==0))){sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName);return 1;}#endifreturn 0;}#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)/*** Evaluate a view and store its result in an ephemeral table. The** pWhere argument is an optional WHERE clause that restricts the** set of rows in the view that are to be added to the ephemeral table.*/void sqlite3MaterializeView(Parse *pParse, /* Parsing context */Table *pView, /* View definition */Expr *pWhere, /* Optional WHERE clause to be added */ExprList *pOrderBy, /* Optional ORDER BY clause */Expr *pLimit, /* Optional LIMIT clause */int iCur /* Cursor number for ephemeral table */){SelectDest dest;Select *pSel;SrcList *pFrom;sqlite3 *db = pParse->db;int iDb = sqlite3SchemaToIndex(db, pView->pSchema);pWhere = sqlite3ExprDup(db, pWhere, 0);pFrom = sqlite3SrcListAppend(pParse, 0, 0, 0);if( pFrom ){assert( pFrom->nSrc==1 );pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);assert( pFrom->a[0].fg.fixedSchema==0 && pFrom->a[0].fg.isSubquery==0 );pFrom->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);assert( pFrom->a[0].fg.isUsing==0 );assert( pFrom->a[0].u3.pOn==0 );}pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy,SF_IncludeHidden, pLimit);sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);sqlite3Select(pParse, pSel, &dest);sqlite3SelectDelete(db, pSel);}#endif /* !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) */#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)/*** Generate an expression tree to implement the WHERE, ORDER BY,** and LIMIT/OFFSET portion of DELETE and UPDATE statements.**** DELETE FROM table_wxyz WHERE a<5 ORDER BY a LIMIT 1;** \__________________________/** pLimitWhere (pInClause)*/Expr *sqlite3LimitWhere(Parse *pParse, /* The parser context */SrcList *pSrc, /* the FROM clause -- which tables to scan */Expr *pWhere, /* The WHERE clause. May be null */ExprList *pOrderBy, /* The ORDER BY clause. May be null */Expr *pLimit, /* The LIMIT clause. May be null */char *zStmtType /* Either DELETE or UPDATE. For err msgs. */){sqlite3 *db = pParse->db;Expr *pLhs = NULL; /* LHS of IN(SELECT...) operator */Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */ExprList *pEList = NULL; /* Expression list containing only pSelectRowid*/SrcList *pSelectSrc = NULL; /* SELECT rowid FROM x ... (dup of pSrc) */Select *pSelect = NULL; /* Complete SELECT tree */Table *pTab;/* Check that there isn't an ORDER BY without a LIMIT clause.*/if( pOrderBy && pLimit==0 ) {sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);sqlite3ExprDelete(pParse->db, pWhere);sqlite3ExprListDelete(pParse->db, pOrderBy);return 0;}/* We only need to generate a select expression if there** is a limit/offset term to enforce.*/if( pLimit == 0 ) {return pWhere;}/* Generate a select expression tree to enforce the limit/offset** term for the DELETE or UPDATE statement. For example:** DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1** becomes:** DELETE FROM table_a WHERE rowid IN (** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1** );*/pTab = pSrc->a[0].pSTab;if( HasRowid(pTab) ){pLhs = sqlite3PExpr(pParse, TK_ROW, 0, 0);pEList = sqlite3ExprListAppend(pParse, 0, sqlite3PExpr(pParse, TK_ROW, 0, 0));}else{Index *pPk = sqlite3PrimaryKeyIndex(pTab);assert( pPk!=0 );assert( pPk->nKeyCol>=1 );if( pPk->nKeyCol==1 ){const char *zName;assert( pPk->aiColumn[0]>=0 && pPk->aiColumn[0]<pTab->nCol );zName = pTab->aCol[pPk->aiColumn[0]].zCnName;pLhs = sqlite3Expr(db, TK_ID, zName);pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, zName));}else{int i;for(i=0; i<pPk->nKeyCol; i++){Expr *p;assert( pPk->aiColumn[i]>=0 && pPk->aiColumn[i]<pTab->nCol );p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zCnName);pEList = sqlite3ExprListAppend(pParse, pEList, p);}pLhs = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);if( pLhs ){pLhs->x.pList = sqlite3ExprListDup(db, pEList, 0);}}}/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree** and the SELECT subtree. */pSrc->a[0].pSTab = 0;pSelectSrc = sqlite3SrcListDup(db, pSrc, 0);pSrc->a[0].pSTab = pTab;if( pSrc->a[0].fg.isIndexedBy ){assert( pSrc->a[0].fg.isCte==0 );pSrc->a[0].u2.pIBIndex = 0;pSrc->a[0].fg.isIndexedBy = 0;sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy);}else if( pSrc->a[0].fg.isCte ){pSrc->a[0].u2.pCteUse->nUse++;}/* generate the SELECT expression tree. */pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0,pOrderBy,0,pLimit);/* now generate the new WHERE rowid IN clause for the DELETE/UPDATE */pInClause = sqlite3PExpr(pParse, TK_IN, pLhs, 0);sqlite3PExprAddSelect(pParse, pInClause, pSelect);return pInClause;}#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) *//* && !defined(SQLITE_OMIT_SUBQUERY) *//*** 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 */ExprList *pOrderBy, /* ORDER BY clause. May be null */Expr *pLimit /* LIMIT clause. May be null */){Vdbe *v; /* The virtual database engine */Table *pTab; /* The table from which records will be deleted */int i; /* Loop counter */WhereInfo *pWInfo; /* Information about the WHERE clause */Index *pIdx; /* For looping over indices of the table */int iTabCur; /* Cursor number for the table */int iDataCur = 0; /* VDBE cursor for the canonical data source */int iIdxCur = 0; /* Cursor number of the first index */int nIdx; /* Number of indices */sqlite3 *db; /* Main database structure */AuthContext sContext; /* Authorization context */NameContext sNC; /* Name context to resolve expressions in */int iDb; /* Database number */int memCnt = 0; /* Memory cell used for change counting */int rcauth; /* Value returned by authorization callback */int eOnePass; /* ONEPASS_OFF or _SINGLE or _MULTI */int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */u8 *aToOpen = 0; /* Open cursor iTabCur+j if aToOpen[j] is true */Index *pPk; /* The PRIMARY KEY index on the table */int iPk = 0; /* First of nPk registers holding PRIMARY KEY value */i16 nPk = 1; /* Number of columns in the PRIMARY KEY */int iKey; /* Memory cell holding key of row to be deleted */i16 nKey; /* Number of memory cells in the row key */int iEphCur = 0; /* Ephemeral table holding all primary key values */int iRowSet = 0; /* Register for rowset of rows to delete */int addrBypass = 0; /* Address of jump over the delete logic */int addrLoop = 0; /* Top of the delete loop */int addrEphOpen = 0; /* Instruction to open the Ephemeral table */int bComplex; /* True if there are triggers or FKs or** subqueries in the WHERE clause */#ifndef SQLITE_OMIT_TRIGGERint isView; /* True if attempting to delete from a view */Trigger *pTrigger; /* List of table triggers, if required */#endifmemset(&sContext, 0, sizeof(sContext));db = pParse->db;assert( db->pParse==pParse );if( pParse->nErr ){goto delete_from_cleanup;}assert( db->mallocFailed==0 );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_TRIGGERpTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);isView = IsView(pTab);#else# define pTrigger 0# define isView 0#endifbComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0);#ifdef SQLITE_OMIT_VIEW# undef isView# define isView 0#endif#if TREETRACE_ENABLEDif( sqlite3TreeTrace & 0x10000 ){sqlite3TreeViewLine(0, "In sqlite3Delete() at %s:%d", __FILE__, __LINE__);sqlite3TreeViewDelete(pParse->pWith, pTabList, pWhere,pOrderBy, pLimit, pTrigger);}#endif#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMITif( !isView ){pWhere = sqlite3LimitWhere(pParse, pTabList, pWhere, pOrderBy, pLimit, "DELETE");pOrderBy = 0;pLimit = 0;}#endif/* If pTab is really a view, make sure it has been initialized.*/if( sqlite3ViewGetColumnNames(pParse, pTab) ){goto delete_from_cleanup;}if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){goto delete_from_cleanup;}iDb = sqlite3SchemaToIndex(db, pTab->pSchema);assert( iDb<db->nDb );rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0,db->aDb[iDb].zDbSName);assert( rcauth==SQLITE_OK || rcauth==SQLITE_DENY || rcauth==SQLITE_IGNORE );if( rcauth==SQLITE_DENY ){goto delete_from_cleanup;}assert(!isView || pTrigger);/* Assign cursor numbers to the table and all its indices.*/assert( pTabList->nSrc==1 );iTabCur = pTabList->a[0].iCursor = pParse->nTab++;for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){pParse->nTab++;}/* 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, bComplex, iDb);/* If we are trying to delete from a view, realize that view into** an ephemeral table.*/#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)if( isView ){sqlite3MaterializeView(pParse, pTab,pWhere, pOrderBy, pLimit, iTabCur);iDataCur = iIdxCur = iTabCur;pOrderBy = 0;pLimit = 0;}#endif/* Resolve the column names in the WHERE clause.*/memset(&sNC, 0, sizeof(sNC));sNC.pParse = pParse;sNC.pSrcList = pTabList;if( sqlite3ResolveExprNames(&sNC, pWhere) ){goto delete_from_cleanup;}/* Initialize the counter of the number of rows deleted, if** we are counting rows.*/if( (db->flags & SQLITE_CountRows)!=0&& !pParse->nested&& !pParse->pTriggerTab&& !pParse->bReturning){memCnt = ++pParse->nMem;sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);}#ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION/* Special case: A DELETE without a WHERE clause deletes everything.** It is easier just to erase the whole table. Prior to version 3.6.5,** this optimization caused the row change count (the value returned by** API function sqlite3_count_changes) to be set incorrectly.**** The "rcauth==SQLITE_OK" terms is the** IMPLEMENTATION-OF: R-17228-37124 If the action code is SQLITE_DELETE and** the callback returns SQLITE_IGNORE then the DELETE operation proceeds but** the truncate optimization is disabled and all rows are deleted** individually.*/if( rcauth==SQLITE_OK&& pWhere==0&& !bComplex&& !IsVirtual(pTab)#ifdef SQLITE_ENABLE_PREUPDATE_HOOK&& db->xPreUpdateCallback==0#endif){assert( !isView );sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);if( HasRowid(pTab) ){sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt ? memCnt : -1,pTab->zName, P4_STATIC);}for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){assert( pIdx->pSchema==pTab->pSchema );if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){sqlite3VdbeAddOp3(v, OP_Clear, pIdx->tnum, iDb, memCnt ? memCnt : -1);}else{sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);}}}else#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */{u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK;if( sNC.ncFlags & NC_Subquery ) bComplex = 1;wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);if( HasRowid(pTab) ){/* For a rowid table, initialize the RowSet to an empty set */pPk = 0;assert( nPk==1 );iRowSet = ++pParse->nMem;sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);}else{/* For a WITHOUT ROWID table, create an ephemeral table used to** hold all primary keys for rows to be deleted. */pPk = sqlite3PrimaryKeyIndex(pTab);assert( pPk!=0 );nPk = pPk->nKeyCol;iPk = pParse->nMem+1;pParse->nMem += nPk;iEphCur = pParse->nTab++;addrEphOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEphCur, nPk);sqlite3VdbeSetP4KeyInfo(pParse, pPk);}/* Construct a query to find the rowid or primary key for every row** to be deleted, based on the WHERE clause. Set variable eOnePass** to indicate the strategy used to implement this delete:**** ONEPASS_OFF: Two-pass approach - use a FIFO for rowids/PK values.** ONEPASS_SINGLE: One-pass approach - at most one row deleted.** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted.*/pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,0,wcf,iTabCur+1);if( pWInfo==0 ) goto delete_from_cleanup;eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI );assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF|| OptimizationDisabled(db, SQLITE_OnePass) );if( eOnePass!=ONEPASS_SINGLE ) sqlite3MultiWrite(pParse);if( sqlite3WhereUsesDeferredSeek(pWInfo) ){sqlite3VdbeAddOp1(v, OP_FinishSeek, iTabCur);}/* Keep track of the number of rows to be deleted */if( memCnt ){sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);}/* Extract the rowid or primary key for the current row */if( pPk ){for(i=0; i<nPk; i++){assert( pPk->aiColumn[i]>=0 );sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur,pPk->aiColumn[i], iPk+i);}iKey = iPk;}else{iKey = ++pParse->nMem;sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, -1, iKey);}if( eOnePass!=ONEPASS_OFF ){/* For ONEPASS, no need to store the rowid/primary-key. There is only** one, so just keep it in its register(s) and fall through to the** delete code. */nKey = nPk; /* OP_Found will use an unpacked key */aToOpen = sqlite3DbMallocRawNN(db, nIdx+2);if( aToOpen==0 ){sqlite3WhereEnd(pWInfo);goto delete_from_cleanup;}memset(aToOpen, 1, nIdx+1);aToOpen[nIdx+1] = 0;if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);addrBypass = sqlite3VdbeMakeLabel(pParse);}else{if( pPk ){/* Add the PK key for this row to the temporary table */iKey = ++pParse->nMem;nKey = 0; /* Zero tells OP_Found to use a composite key */sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,sqlite3IndexAffinityStr(pParse->db, pPk), nPk);sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEphCur, iKey, iPk, nPk);}else{/* Add the rowid of the row to be deleted to the RowSet */nKey = 1; /* OP_DeferredSeek always uses a single rowid */sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);}sqlite3WhereEnd(pWInfo);}/* Unless this is a view, open cursors for the table we are** deleting from and all its indices. If this is a view, then the** only effect this statement has is to fire the INSTEAD OF** triggers.*/if( !isView ){int iAddrOnce = 0;if( eOnePass==ONEPASS_MULTI ){iAddrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);}testcase( IsVirtual(pTab) );sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, OPFLAG_FORDELETE,iTabCur, aToOpen, &iDataCur, &iIdxCur);assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );if( eOnePass==ONEPASS_MULTI ){sqlite3VdbeJumpHereOrPopInst(v, iAddrOnce);}}/* Set up a loop over the rowids/primary-keys that were found in the** where-clause loop above.*/if( eOnePass!=ONEPASS_OFF ){assert( nKey==nPk ); /* OP_Found will use an unpacked key */if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){assert( pPk!=0 || IsView(pTab) );sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);VdbeCoverage(v);}}else if( pPk ){addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v);if( IsVirtual(pTab) ){sqlite3VdbeAddOp3(v, OP_Column, iEphCur, 0, iKey);}else{sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey);}assert( nKey==0 ); /* OP_Found will use a composite key */}else{addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey);VdbeCoverage(v);assert( nKey==1 );}/* Delete the row */#ifndef SQLITE_OMIT_VIRTUALTABLEif( IsVirtual(pTab) ){const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);sqlite3VtabMakeWritable(pParse, pTab);assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );sqlite3MayAbort(pParse);if( eOnePass==ONEPASS_SINGLE ){sqlite3VdbeAddOp1(v, OP_Close, iTabCur);if( sqlite3IsToplevel(pParse) ){pParse->isMultiWrite = 0;}}sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);sqlite3VdbeChangeP5(v, OE_Abort);}else#endif{int count = (pParse->nested==0); /* True to count changes */sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,iKey, nKey, count, OE_Default, eOnePass, aiCurOnePass[1]);}/* End of the loop over all rowids/primary-keys. */if( eOnePass!=ONEPASS_OFF ){sqlite3VdbeResolveLabel(v, addrBypass);sqlite3WhereEnd(pWInfo);}else if( pPk ){sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v);sqlite3VdbeJumpHere(v, addrLoop);}else{sqlite3VdbeGoto(v, addrLoop);sqlite3VdbeJumpHere(v, addrLoop);}} /* End non-truncate path *//* Update the sqlite_sequence table by storing the content of the** maximum rowid counter values recorded while inserting into** autoincrement tables.*/if( pParse->nested==0 && pParse->pTriggerTab==0 ){sqlite3AutoincrementEnd(pParse);}/* 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( memCnt ){sqlite3CodeChangeCount(v, memCnt, "rows deleted");}delete_from_cleanup:sqlite3AuthContextPop(&sContext);sqlite3SrcListDelete(db, pTabList);sqlite3ExprDelete(db, pWhere);#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)sqlite3ExprListDelete(db, pOrderBy);sqlite3ExprDelete(db, pLimit);#endifif( aToOpen ) sqlite3DbNNFreeNN(db, aToOpen);return;}/* Make sure "isView" and other macros defined above are undefined. Otherwise** they may interfere with compilation of other functions in this file** (or in another file, if this file becomes part of the amalgamation). */#ifdef isView#undef isView#endif#ifdef pTrigger#undef pTrigger#endif/*** This routine generates VDBE code that causes a single row of a** single table to be deleted. Both the original table entry and** all indices are removed.**** Preconditions:**** 1. iDataCur is an open cursor on the btree that is the canonical data** store for the table. (This will be either the table itself,** in the case of a rowid table, or the PRIMARY KEY index in the case** of a WITHOUT ROWID table.)**** 2. Read/write cursors for all indices of pTab must be open as** cursor number iIdxCur+i for the i-th index.**** 3. The primary key for the row to be deleted must be stored in a** sequence of nPk memory cells starting at iPk. If nPk==0 that means** that a search record formed from OP_MakeRecord is contained in the** single memory location iPk.**** eMode:** Parameter eMode may be passed either ONEPASS_OFF (0), ONEPASS_SINGLE, or** ONEPASS_MULTI. If eMode is not ONEPASS_OFF, then the cursor** iDataCur already points to the row to delete. If eMode is ONEPASS_OFF** then this function must seek iDataCur to the entry identified by iPk** and nPk before reading from it.**** If eMode is ONEPASS_MULTI, then this call is being made as part** of a ONEPASS delete that affects multiple rows. In this case, if** iIdxNoSeek is a valid cursor number (>=0) and is not the same as** iDataCur, then its position should be preserved following the delete** operation. Or, if iIdxNoSeek is not a valid cursor number, the** position of iDataCur should be preserved instead.**** iIdxNoSeek:** If iIdxNoSeek is a valid cursor number (>=0) not equal to iDataCur,** then it identifies an index cursor (from within array of cursors** starting at iIdxCur) that already points to the index entry to be deleted.** Except, this optimization is disabled if there are BEFORE triggers since** the trigger body might have moved the cursor.*/void sqlite3GenerateRowDelete(Parse *pParse, /* Parsing context */Table *pTab, /* Table containing the row to be deleted */Trigger *pTrigger, /* List of triggers to (potentially) fire */int iDataCur, /* Cursor from which column data is extracted */int iIdxCur, /* First index cursor */int iPk, /* First memory cell containing the PRIMARY KEY */i16 nPk, /* Number of PRIMARY KEY memory cells */u8 count, /* If non-zero, increment the row change counter */u8 onconf, /* Default ON CONFLICT policy for triggers */u8 eMode, /* ONEPASS_OFF, _SINGLE, or _MULTI. See above */int iIdxNoSeek /* Cursor number of cursor that does not need seeking */){Vdbe *v = pParse->pVdbe; /* Vdbe */int iOld = 0; /* First register in OLD.* array */int iLabel; /* Label resolved to end of generated code */u8 opSeek; /* Seek opcode *//* Vdbe is guaranteed to have been allocated by this stage. */assert( v );VdbeModuleComment((v, "BEGIN: GenRowDel(%d,%d,%d,%d)",iDataCur, iIdxCur, iPk, (int)nPk));/* Seek cursor iCur to the row to delete. If this row no longer exists** (this can happen if a trigger program has already deleted it), do** not attempt to delete it or fire any DELETE triggers. */iLabel = sqlite3VdbeMakeLabel(pParse);opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;if( eMode==ONEPASS_OFF ){sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);VdbeCoverageIf(v, opSeek==OP_NotExists);VdbeCoverageIf(v, opSeek==OP_NotFound);}/* If there are any triggers to fire, allocate a range of registers to** use for the old.* references in the triggers. */if( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){u32 mask; /* Mask of OLD.* columns in use */int iCol; /* Iterator used while populating OLD.* */int addrStart; /* Start of BEFORE trigger programs *//* TODO: Could use temporary registers here. Also could attempt to** avoid copying the contents of the rowid register. */mask = sqlite3TriggerColmask(pParse, pTrigger, 0, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onconf);mask |= sqlite3FkOldmask(pParse, pTab);iOld = pParse->nMem+1;pParse->nMem += (1 + pTab->nCol);/* Populate the OLD.* pseudo-table register array. These values will be** used by any BEFORE and AFTER triggers that exist. */sqlite3VdbeAddOp2(v, OP_Copy, iPk, iOld);for(iCol=0; iCol<pTab->nCol; iCol++){testcase( mask!=0xffffffff && iCol==31 );testcase( mask!=0xffffffff && iCol==32 );if( mask==0xffffffff || (iCol<=31 && (mask & MASKBIT32(iCol))!=0) ){int kk = sqlite3TableColumnToStorage(pTab, iCol);sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, iCol, iOld+kk+1);}}/* Invoke BEFORE DELETE trigger programs. */addrStart = sqlite3VdbeCurrentAddr(v);sqlite3CodeRowTrigger(pParse, pTrigger,TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel);/* If any BEFORE triggers were coded, then seek the cursor to the** row to be deleted again. It may be that the BEFORE triggers moved** the cursor or already deleted the row that the cursor was** pointing to.**** Also disable the iIdxNoSeek optimization since the BEFORE trigger** may have moved that cursor.*/if( addrStart<sqlite3VdbeCurrentAddr(v) ){sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);VdbeCoverageIf(v, opSeek==OP_NotExists);VdbeCoverageIf(v, opSeek==OP_NotFound);testcase( iIdxNoSeek>=0 );iIdxNoSeek = -1;}/* Do FK processing. This call checks that any FK constraints that** refer to this table (i.e. constraints attached to other tables)** are not violated by deleting this row. */sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0);}/* Delete the index and table entries. Skip this step if pTab is really** a view (in which case the only effect of the DELETE statement is to** fire the INSTEAD OF triggers).**** If variable 'count' is non-zero, then this OP_Delete instruction should** invoke the update-hook. The pre-update-hook, on the other hand should** be invoked unless table pTab is a system table. The difference is that** the update-hook is not invoked for rows removed by REPLACE, but the** pre-update-hook is.*/if( !IsView(pTab) ){u8 p5 = 0;sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));if( pParse->nested==0 || 0==sqlite3_stricmp(pTab->zName, "sqlite_stat1") ){sqlite3VdbeAppendP4(v, (char*)pTab, P4_TABLE);}if( eMode!=ONEPASS_OFF ){sqlite3VdbeChangeP5(v, OPFLAG_AUXDELETE);}if( iIdxNoSeek>=0 && iIdxNoSeek!=iDataCur ){sqlite3VdbeAddOp1(v, OP_Delete, iIdxNoSeek);}if( eMode==ONEPASS_MULTI ) p5 |= OPFLAG_SAVEPOSITION;sqlite3VdbeChangeP5(v, p5);}/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to** handle rows (possibly in other tables) that refer via a foreign key** to the row just deleted. */sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0);/* Invoke AFTER DELETE trigger programs. */if( pTrigger ){sqlite3CodeRowTrigger(pParse, pTrigger,TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel);}/* Jump here if the row had already been deleted before any BEFORE** trigger programs were invoked. Or if a trigger program throws a** RAISE(IGNORE) exception. */sqlite3VdbeResolveLabel(v, iLabel);VdbeModuleComment((v, "END: GenRowDel()"));}/*** This routine generates VDBE code that causes the deletion of all** index entries associated with a single row of a single table, pTab**** Preconditions:**** 1. A read/write cursor "iDataCur" must be open on the canonical storage** btree for the table pTab. (This will be either the table itself** for rowid tables or to the primary key index for WITHOUT ROWID** tables.)**** 2. Read/write cursors for all indices of pTab must be open as** cursor number iIdxCur+i for the i-th index. (The pTab->pIndex** index is the 0-th index.)**** 3. The "iDataCur" cursor must be already be positioned on the row** that is to be deleted.*/void sqlite3GenerateRowIndexDelete(Parse *pParse, /* Parsing and code generating context */Table *pTab, /* Table containing the row to be deleted */int iDataCur, /* Cursor of table holding data. */int iIdxCur, /* First index cursor */int *aRegIdx, /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */int iIdxNoSeek /* Do not delete from this cursor */){int i; /* Index loop counter */int r1 = -1; /* Register holding an index key */int iPartIdxLabel; /* Jump destination for skipping partial index entries */Index *pIdx; /* Current index */Index *pPrior = 0; /* Prior index */Vdbe *v; /* The prepared statement under construction */Index *pPk; /* PRIMARY KEY index, or NULL for rowid tables */v = pParse->pVdbe;pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){assert( iIdxCur+i!=iDataCur || pPk==pIdx );if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;if( pIdx==pPk ) continue;if( iIdxCur+i==iIdxNoSeek ) continue;VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,&iPartIdxLabel, pPrior, r1);sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);pPrior = pIdx;}}/*** Generate code that will assemble an index key and stores it in register** regOut. 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. If pTab is a WITHOUT ROWID table, then** iCur must be the cursor of the PRIMARY KEY index.**** Return a register number which is the first in a block of** registers that holds the elements of the index key. The** block of registers has already been deallocated by the time** this routine returns.**** If *piPartIdxLabel is not NULL, fill it in with a label and jump** to that label if pIdx is a partial index that should be skipped.** The label should be resolved using sqlite3ResolvePartIdxLabel().** A partial index should be skipped if its WHERE clause evaluates** to false or null. If pIdx is not a partial index, *piPartIdxLabel** will be set to zero which is an empty label that is ignored by** sqlite3ResolvePartIdxLabel().**** The pPrior and regPrior parameters are used to implement a cache to** avoid unnecessary register loads. If pPrior is not NULL, then it is** a pointer to a different index for which an index key has just been** computed into register regPrior. If the current pIdx index is generating** its key into the same sequence of registers and if pPrior and pIdx share** a column in common, then the register corresponding to that column already** holds the correct value and the loading of that register is skipped.** This optimization is helpful when doing a DELETE or an INTEGRITY_CHECK** on a table with multiple indices, and especially with the ROWID or** PRIMARY KEY columns of the index.*/int sqlite3GenerateIndexKey(Parse *pParse, /* Parsing context */Index *pIdx, /* The index for which to generate a key */int iDataCur, /* Cursor number from which to take column data */int regOut, /* Put the new key into this register if not 0 */int prefixOnly, /* Compute only a unique prefix of the key */int *piPartIdxLabel, /* OUT: Jump to this label to skip partial index */Index *pPrior, /* Previously generated index key */int regPrior /* Register holding previous generated key */){Vdbe *v = pParse->pVdbe;int j;int regBase;int nCol;if( piPartIdxLabel ){if( pIdx->pPartIdxWhere ){*piPartIdxLabel = sqlite3VdbeMakeLabel(pParse);pParse->iSelfTab = iDataCur + 1;sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,SQLITE_JUMPIFNULL);pParse->iSelfTab = 0;pPrior = 0; /* Ticket a9efb42811fa41ee 2019年11月02日;** pPartIdxWhere may have corrupted regPrior registers */}else{*piPartIdxLabel = 0;}}nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn;regBase = sqlite3GetTempRange(pParse, nCol);if( pPrior && (regBase!=regPrior || pPrior->pPartIdxWhere) ) pPrior = 0;for(j=0; j<nCol; j++){if( pPrior&& pPrior->aiColumn[j]==pIdx->aiColumn[j]&& pPrior->aiColumn[j]!=XN_EXPR){/* This column was already computed by the previous index */continue;}sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j);if( pIdx->aiColumn[j]>=0 ){/* If the column affinity is REAL but the number is an integer, then it** might be stored in the table as an integer (using a compact** representation) then converted to REAL by an OP_RealAffinity opcode.** But we are getting ready to store this value back into an index, where** it should be converted by to INTEGER again. So omit the** OP_RealAffinity opcode if it is present */sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity);}}if( regOut ){sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut);}sqlite3ReleaseTempRange(pParse, regBase, nCol);return regBase;}/*** If a prior call to sqlite3GenerateIndexKey() generated a jump-over label** because it was a partial index, then this routine should be called to** resolve that label.*/void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){if( iLabel ){sqlite3VdbeResolveLabel(pParse->pVdbe, iLabel);}}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。