Index: third_party/sqlite/src/src/delete.c |
diff --git a/third_party/sqlite/src/src/delete.c b/third_party/sqlite/src/src/delete.c |
index d81dd3f6b48d8eab35a073dcceedbd8b1555550a..ed273bde81aab798020206a4b7445ce3fbe7d1f5 100644 |
--- a/third_party/sqlite/src/src/delete.c |
+++ b/third_party/sqlite/src/src/delete.c |
@@ -106,7 +106,8 @@ void sqlite3MaterializeView( |
assert( pFrom->a[0].pOn==0 ); |
assert( pFrom->a[0].pUsing==0 ); |
} |
- pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0); |
+ pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, |
+ SF_IncludeHidden, 0, 0); |
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur); |
sqlite3Select(pParse, pSel, &dest); |
sqlite3SelectDelete(db, pSel); |
@@ -189,7 +190,7 @@ Expr *sqlite3LimitWhere( |
pInClause->x.pSelect = pSelect; |
pInClause->flags |= EP_xIsSelect; |
- sqlite3ExprSetHeight(pParse, pInClause); |
+ sqlite3ExprSetHeightAndFlags(pParse, pInClause); |
return pInClause; |
/* something went wrong. clean up anything allocated. */ |
@@ -226,8 +227,8 @@ void sqlite3DeleteFrom( |
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; /* VDBE cursor for the canonical data source */ |
- int iIdxCur; /* Cursor number of the first index */ |
+ 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 */ |
@@ -235,7 +236,7 @@ void sqlite3DeleteFrom( |
int iDb; /* Database number */ |
int memCnt = -1; /* Memory cell used for change counting */ |
int rcauth; /* Value returned by authorization callback */ |
- int okOnePass; /* True for one-pass algorithm without the FIFO */ |
+ 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 */ |
@@ -247,12 +248,12 @@ void sqlite3DeleteFrom( |
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 addrDelete = 0; /* Jump directly to the delete logic */ |
int addrEphOpen = 0; /* Instruction to open the Ephemeral table */ |
#ifndef SQLITE_OMIT_TRIGGER |
int isView; /* True if attempting to delete from a view */ |
Trigger *pTrigger; /* List of table triggers, if required */ |
+ int bComplex; /* True if there are either triggers or FKs */ |
#endif |
memset(&sContext, 0, sizeof(sContext)); |
@@ -276,9 +277,11 @@ void sqlite3DeleteFrom( |
#ifndef SQLITE_OMIT_TRIGGER |
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); |
isView = pTab->pSelect!=0; |
+ bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0); |
#else |
# define pTrigger 0 |
# define isView 0 |
+# define bComplex 0 |
#endif |
#ifdef SQLITE_OMIT_VIEW |
# undef isView |
@@ -359,8 +362,10 @@ void sqlite3DeleteFrom( |
** 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. */ |
- if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) |
- && 0==sqlite3FkRequired(pParse, pTab, 0, 0) |
+ if( rcauth==SQLITE_OK |
+ && pWhere==0 |
+ && !bComplex |
+ && !IsVirtual(pTab) |
){ |
assert( !isView ); |
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName); |
@@ -375,6 +380,8 @@ void sqlite3DeleteFrom( |
}else |
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ |
{ |
+ u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK; |
+ wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW); |
if( HasRowid(pTab) ){ |
/* For a rowid table, initialize the RowSet to an empty set */ |
pPk = 0; |
@@ -395,13 +402,18 @@ void sqlite3DeleteFrom( |
} |
/* Construct a query to find the rowid or primary key for every row |
- ** to be deleted, based on the WHERE clause. |
+ ** 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, |
- WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK, |
- iTabCur+1); |
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1); |
if( pWInfo==0 ) goto delete_from_cleanup; |
- okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); |
+ eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); |
+ assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI ); |
+ assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF ); |
/* Keep track of the number of rows to be deleted */ |
if( db->flags & SQLITE_CountRows ){ |
@@ -411,6 +423,7 @@ void sqlite3DeleteFrom( |
/* 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); |
} |
@@ -421,11 +434,10 @@ void sqlite3DeleteFrom( |
if( iKey>pParse->nMem ) pParse->nMem = iKey; |
} |
- if( okOnePass ){ |
- /* For ONEPASS, no need to store the rowid/primary-key. There is only |
+ 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. |
- */ |
+ ** delete code. */ |
nKey = nPk; /* OP_Found will use an unpacked key */ |
aToOpen = sqlite3DbMallocRaw(db, nIdx+2); |
if( aToOpen==0 ){ |
@@ -437,27 +449,27 @@ void sqlite3DeleteFrom( |
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0; |
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0; |
if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen); |
- addrDelete = sqlite3VdbeAddOp0(v, OP_Goto); /* Jump to DELETE logic */ |
- }else if( pPk ){ |
- /* Construct a composite key for the row to be deleted and remember it */ |
- iKey = ++pParse->nMem; |
- nKey = 0; /* Zero tells OP_Found to use a composite key */ |
- sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey, |
- sqlite3IndexAffinityStr(v, pPk), nPk); |
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey); |
}else{ |
- /* Get the rowid of the row to be deleted and remember it in the RowSet */ |
- nKey = 1; /* OP_Seek always uses a single rowid */ |
- sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey); |
+ 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); |
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey); |
+ }else{ |
+ /* Add the rowid of the row to be deleted to the RowSet */ |
+ nKey = 1; /* OP_Seek always uses a single rowid */ |
+ sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey); |
+ } |
} |
- /* End of the WHERE loop */ |
- sqlite3WhereEnd(pWInfo); |
- if( okOnePass ){ |
- /* Bypass the delete logic below if the WHERE loop found zero rows */ |
+ /* If this DELETE cannot use the ONEPASS strategy, this is the |
+ ** end of the WHERE loop */ |
+ if( eOnePass!=ONEPASS_OFF ){ |
addrBypass = sqlite3VdbeMakeLabel(v); |
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBypass); |
- sqlite3VdbeJumpHere(v, addrDelete); |
+ }else{ |
+ sqlite3WhereEnd(pWInfo); |
} |
/* Unless this is a view, open cursors for the table we are |
@@ -466,21 +478,25 @@ void sqlite3DeleteFrom( |
** triggers. |
*/ |
if( !isView ){ |
+ int iAddrOnce = 0; |
+ u8 p5 = (eOnePass==ONEPASS_OFF ? 0 : OPFLAG_FORDELETE); |
+ if( eOnePass==ONEPASS_MULTI ){ |
+ iAddrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v); |
+ } |
testcase( IsVirtual(pTab) ); |
- sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen, |
- &iDataCur, &iIdxCur); |
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, p5, iTabCur, |
+ aToOpen, &iDataCur, &iIdxCur); |
assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur ); |
assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 ); |
+ if( eOnePass==ONEPASS_MULTI ) sqlite3VdbeJumpHere(v, iAddrOnce); |
} |
/* Set up a loop over the rowids/primary-keys that were found in the |
** where-clause loop above. |
*/ |
- if( okOnePass ){ |
- /* Just one row. Hence the top-of-loop is a no-op */ |
+ if( eOnePass!=ONEPASS_OFF ){ |
assert( nKey==nPk ); /* OP_Found will use an unpacked key */ |
- assert( !IsVirtual(pTab) ); |
- if( aToOpen[iDataCur-iTabCur] ){ |
+ if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){ |
assert( pPk!=0 || pTab->pSelect!=0 ); |
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); |
VdbeCoverage(v); |
@@ -502,23 +518,32 @@ void sqlite3DeleteFrom( |
sqlite3VtabMakeWritable(pParse, pTab); |
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB); |
sqlite3VdbeChangeP5(v, OE_Abort); |
+ assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE ); |
sqlite3MayAbort(pParse); |
+ if( eOnePass==ONEPASS_SINGLE && sqlite3IsToplevel(pParse) ){ |
+ pParse->isMultiWrite = 0; |
+ } |
}else |
#endif |
{ |
int count = (pParse->nested==0); /* True to count changes */ |
+ int iIdxNoSeek = -1; |
+ if( bComplex==0 && aiCurOnePass[1]!=iDataCur ){ |
+ iIdxNoSeek = aiCurOnePass[1]; |
+ } |
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, |
- iKey, nKey, count, OE_Default, okOnePass); |
+ iKey, nKey, count, OE_Default, eOnePass, iIdxNoSeek); |
} |
/* End of the loop over all rowids/primary-keys. */ |
- if( okOnePass ){ |
+ 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{ |
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrLoop); |
+ sqlite3VdbeGoto(v, addrLoop); |
sqlite3VdbeJumpHere(v, addrLoop); |
} |
@@ -585,6 +610,25 @@ delete_from_cleanup: |
** 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), 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), then it identifies an |
+** index cursor (from within array of cursors starting at iIdxCur) that |
+** already points to the index entry to be deleted. |
*/ |
void sqlite3GenerateRowDelete( |
Parse *pParse, /* Parsing context */ |
@@ -596,7 +640,8 @@ void sqlite3GenerateRowDelete( |
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 bNoSeek /* iDataCur is already pointing to the row to delete */ |
+ 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 */ |
@@ -613,7 +658,7 @@ void sqlite3GenerateRowDelete( |
** not attempt to delete it or fire any DELETE triggers. */ |
iLabel = sqlite3VdbeMakeLabel(v); |
opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound; |
- if( !bNoSeek ){ |
+ if( eMode==ONEPASS_OFF ){ |
sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk); |
VdbeCoverageIf(v, opSeek==OP_NotExists); |
VdbeCoverageIf(v, opSeek==OP_NotFound); |
@@ -673,11 +718,15 @@ void sqlite3GenerateRowDelete( |
** a view (in which case the only effect of the DELETE statement is to |
** fire the INSTEAD OF triggers). */ |
if( pTab->pSelect==0 ){ |
- sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0); |
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek); |
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0)); |
if( count ){ |
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); |
} |
+ if( iIdxNoSeek>=0 ){ |
+ sqlite3VdbeAddOp1(v, OP_Delete, iIdxNoSeek); |
+ } |
+ sqlite3VdbeChangeP5(v, eMode==ONEPASS_MULTI); |
} |
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to |
@@ -720,7 +769,8 @@ void sqlite3GenerateRowIndexDelete( |
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 *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 */ |
@@ -736,11 +786,12 @@ void sqlite3GenerateRowIndexDelete( |
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); |
+ &iPartIdxLabel, pPrior, r1); |
sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, |
- pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); |
+ pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); |
sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); |
pPrior = pIdx; |
} |
@@ -789,17 +840,16 @@ int sqlite3GenerateIndexKey( |
){ |
Vdbe *v = pParse->pVdbe; |
int j; |
- Table *pTab = pIdx->pTable; |
int regBase; |
int nCol; |
if( piPartIdxLabel ){ |
if( pIdx->pPartIdxWhere ){ |
*piPartIdxLabel = sqlite3VdbeMakeLabel(v); |
- pParse->iPartIdxTab = iDataCur; |
+ pParse->iSelfTab = iDataCur; |
sqlite3ExprCachePush(pParse); |
- sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel, |
- SQLITE_JUMPIFNULL); |
+ sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel, |
+ SQLITE_JUMPIFNULL); |
}else{ |
*piPartIdxLabel = 0; |
} |
@@ -808,9 +858,14 @@ int sqlite3GenerateIndexKey( |
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] ) continue; |
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j], |
- regBase+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 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. |