| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include "sqliteInt.h" |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
|
|
| |
| |
| |
| 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); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int vtabIsReadOnly(Parse *pParse, Table *pTab){ |
| assert( IsVirtual(pTab) ); |
| if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){ |
| return 1; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| if( pParse->pToplevel!=0 |
| && 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); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| 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_VIEW |
| if( IsView(pTab) |
| && (pTrigger==0 || (pTrigger->bReturning && pTrigger->pNext==0)) |
| ){ |
| sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); |
| return 1; |
| } |
| #endif |
| return 0; |
| } |
|
|
|
|
| #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) |
| |
| |
| |
| |
| |
| void sqlite3MaterializeView( |
| Parse *pParse, |
| Table *pView, |
| Expr *pWhere, |
| ExprList *pOrderBy, |
| Expr *pLimit, |
| int iCur |
| ){ |
| 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 |
|
|
| #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) |
| |
| |
| |
| |
| |
| |
| |
| |
| Expr *sqlite3LimitWhere( |
| Parse *pParse, |
| SrcList *pSrc, |
| Expr *pWhere, |
| ExprList *pOrderBy, |
| Expr *pLimit, |
| char *zStmtType |
| ){ |
| sqlite3 *db = pParse->db; |
| Expr *pLhs = NULL; |
| Expr *pInClause = NULL; |
| ExprList *pEList = NULL; |
| SrcList *pSelectSrc = NULL; |
| Select *pSelect = NULL; |
| Table *pTab; |
|
|
| |
| |
| if( pOrderBy && pLimit==0 ) { |
| sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType); |
| sqlite3ExprDelete(pParse->db, pWhere); |
| sqlite3ExprListDelete(pParse->db, pOrderBy); |
| return 0; |
| } |
|
|
| |
| |
| |
| if( pLimit == 0 ) { |
| return pWhere; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| 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); |
| } |
| } |
| } |
|
|
| |
| |
| 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++; |
| } |
|
|
| |
| pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0, |
| pOrderBy,0,pLimit |
| ); |
|
|
| |
| pInClause = sqlite3PExpr(pParse, TK_IN, pLhs, 0); |
| sqlite3PExprAddSelect(pParse, pInClause, pSelect); |
| return pInClause; |
| } |
| #endif |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| void sqlite3DeleteFrom( |
| Parse *pParse, |
| SrcList *pTabList, |
| Expr *pWhere, |
| ExprList *pOrderBy, |
| Expr *pLimit |
| ){ |
| Vdbe *v; |
| Table *pTab; |
| int i; |
| WhereInfo *pWInfo; |
| Index *pIdx; |
| int iTabCur; |
| int iDataCur = 0; |
| int iIdxCur = 0; |
| int nIdx; |
| sqlite3 *db; |
| AuthContext sContext; |
| NameContext sNC; |
| int iDb; |
| int memCnt = 0; |
| int rcauth; |
| int eOnePass; |
| int aiCurOnePass[2]; |
| u8 *aToOpen = 0; |
| Index *pPk; |
| int iPk = 0; |
| i16 nPk = 1; |
| int iKey; |
| i16 nKey; |
| int iEphCur = 0; |
| int iRowSet = 0; |
| int addrBypass = 0; |
| int addrLoop = 0; |
| int addrEphOpen = 0; |
| int bComplex; |
| |
|
|
| #ifndef SQLITE_OMIT_TRIGGER |
| int isView; |
| Trigger *pTrigger; |
| #endif |
|
|
| memset(&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 ); |
|
|
| |
| |
| |
| |
| |
| pTab = sqlite3SrcListLookup(pParse, pTabList); |
| if( pTab==0 ) goto delete_from_cleanup; |
|
|
| |
| |
| |
| #ifndef SQLITE_OMIT_TRIGGER |
| pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); |
| isView = IsView(pTab); |
| #else |
| # define pTrigger 0 |
| # define isView 0 |
| #endif |
| bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0); |
| #ifdef SQLITE_OMIT_VIEW |
| # undef isView |
| # define isView 0 |
| #endif |
|
|
| #if TREETRACE_ENABLED |
| if( 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_LIMIT |
| if( !isView ){ |
| pWhere = sqlite3LimitWhere( |
| pParse, pTabList, pWhere, pOrderBy, pLimit, "DELETE" |
| ); |
| pOrderBy = 0; |
| pLimit = 0; |
| } |
| #endif |
|
|
| |
| |
| 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); |
|
|
| |
| |
| assert( pTabList->nSrc==1 ); |
| iTabCur = pTabList->a[0].iCursor = pParse->nTab++; |
| for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ |
| pParse->nTab++; |
| } |
|
|
| |
| |
| if( isView ){ |
| sqlite3AuthContextPush(pParse, &sContext, pTab->zName); |
| } |
|
|
| |
| |
| v = sqlite3GetVdbe(pParse); |
| if( v==0 ){ |
| goto delete_from_cleanup; |
| } |
| if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); |
| sqlite3BeginWriteOperation(pParse, bComplex, iDb); |
|
|
| |
| |
| |
| #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 |
|
|
| |
| |
| memset(&sNC, 0, sizeof(sNC)); |
| sNC.pParse = pParse; |
| sNC.pSrcList = pTabList; |
| if( sqlite3ResolveExprNames(&sNC, pWhere) ){ |
| goto delete_from_cleanup; |
| } |
|
|
| |
| |
| |
| 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 |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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 |
| { |
| u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK; |
| if( sNC.ncFlags & NC_Subquery ) bComplex = 1; |
| wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW); |
| if( HasRowid(pTab) ){ |
| |
| pPk = 0; |
| assert( nPk==1 ); |
| iRowSet = ++pParse->nMem; |
| sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); |
| }else{ |
| |
| |
| 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); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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); |
| } |
| |
| |
| if( memCnt ){ |
| sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); |
| } |
| |
| |
| 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 ){ |
| |
| |
| |
| nKey = nPk; |
| 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 ){ |
| |
| iKey = ++pParse->nMem; |
| nKey = 0; |
| sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey, |
| sqlite3IndexAffinityStr(pParse->db, pPk), nPk); |
| sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEphCur, iKey, iPk, nPk); |
| }else{ |
| |
| nKey = 1; |
| sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey); |
| } |
| sqlite3WhereEnd(pWInfo); |
| } |
| |
| |
| |
| |
| |
| |
| 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); |
| } |
| } |
| |
| |
| |
| |
| if( eOnePass!=ONEPASS_OFF ){ |
| assert( nKey==nPk ); |
| 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 ); |
| }else{ |
| addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey); |
| VdbeCoverage(v); |
| assert( nKey==1 ); |
| } |
| |
| |
| #ifndef SQLITE_OMIT_VIRTUALTABLE |
| if( 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); |
| sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, |
| iKey, nKey, count, OE_Default, eOnePass, aiCurOnePass[1]); |
| } |
| |
| |
| 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); |
| } |
| } |
|
|
| |
| |
| |
| |
| if( pParse->nested==0 && pParse->pTriggerTab==0 ){ |
| sqlite3AutoincrementEnd(pParse); |
| } |
|
|
| |
| |
| |
| |
| 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); |
| #endif |
| if( aToOpen ) sqlite3DbNNFreeNN(db, aToOpen); |
| return; |
| } |
| |
| |
| |
| #ifdef isView |
| #undef isView |
| #endif |
| #ifdef pTrigger |
| #undef pTrigger |
| #endif |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void sqlite3GenerateRowDelete( |
| Parse *pParse, |
| Table *pTab, |
| Trigger *pTrigger, |
| int iDataCur, |
| int iIdxCur, |
| int iPk, |
| i16 nPk, |
| u8 count, |
| u8 onconf, |
| u8 eMode, |
| int iIdxNoSeek |
| ){ |
| Vdbe *v = pParse->pVdbe; |
| int iOld = 0; |
| int iLabel; |
| u8 opSeek; |
|
|
| |
| assert( v ); |
| VdbeModuleComment((v, "BEGIN: GenRowDel(%d,%d,%d,%d)", |
| iDataCur, iIdxCur, iPk, (int)nPk)); |
|
|
| |
| |
| |
| 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( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){ |
| u32 mask; |
| int iCol; |
| int addrStart; |
|
|
| |
| |
| 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); |
|
|
| |
| |
| 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); |
| } |
| } |
|
|
| |
| addrStart = sqlite3VdbeCurrentAddr(v); |
| sqlite3CodeRowTrigger(pParse, pTrigger, |
| TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel |
| ); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
|
|
| |
| |
| |
| sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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); |
| } |
|
|
| |
| |
| |
| sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0); |
|
|
| |
| if( pTrigger ){ |
| sqlite3CodeRowTrigger(pParse, pTrigger, |
| TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel |
| ); |
| } |
|
|
| |
| |
| |
| sqlite3VdbeResolveLabel(v, iLabel); |
| VdbeModuleComment((v, "END: GenRowDel()")); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void sqlite3GenerateRowIndexDelete( |
| Parse *pParse, |
| Table *pTab, |
| int iDataCur, |
| int iIdxCur, |
| int *aRegIdx, |
| int iIdxNoSeek |
| ){ |
| int i; |
| int r1 = -1; |
| int iPartIdxLabel; |
| Index *pIdx; |
| Index *pPrior = 0; |
| Vdbe *v; |
| Index *pPk; |
|
|
| 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); |
| sqlite3VdbeChangeP5(v, 1); |
| sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); |
| pPrior = pIdx; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int sqlite3GenerateIndexKey( |
| Parse *pParse, |
| Index *pIdx, |
| int iDataCur, |
| int regOut, |
| int prefixOnly, |
| int *piPartIdxLabel, |
| Index *pPrior, |
| int regPrior |
| ){ |
| 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; |
| |
| }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 |
| ){ |
| |
| continue; |
| } |
| sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j); |
| if( pIdx->aiColumn[j]>=0 ){ |
| |
| |
| |
| |
| |
| |
| sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity); |
| } |
| } |
| if( regOut ){ |
| sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut); |
| } |
| sqlite3ReleaseTempRange(pParse, regBase, nCol); |
| return regBase; |
| } |
|
|
| |
| |
| |
| |
| |
| void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){ |
| if( iLabel ){ |
| sqlite3VdbeResolveLabel(pParse->pVdbe, iLabel); |
| } |
| } |
|
|