| Index: third_party/sqlite/sqlite-src-3100200/src/delete.c
|
| diff --git a/third_party/sqlite/src/src/delete.c b/third_party/sqlite/sqlite-src-3100200/src/delete.c
|
| similarity index 85%
|
| copy from third_party/sqlite/src/src/delete.c
|
| copy to third_party/sqlite/sqlite-src-3100200/src/delete.c
|
| index d81dd3f6b48d8eab35a073dcceedbd8b1555550a..ed273bde81aab798020206a4b7445ce3fbe7d1f5 100644
|
| --- a/third_party/sqlite/src/src/delete.c
|
| +++ b/third_party/sqlite/sqlite-src-3100200/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.
|
|
|