Index: src/delete.c ================================================================== --- src/delete.c +++ src/delete.c @@ -225,24 +225,38 @@ Expr *pWhere /* The WHERE clause. May be null */ ){ Vdbe *v; /* The virtual database engine */ Table *pTab; /* The table from which records will be deleted */ const char *zDb; /* Name of database holding pTab */ - int end, addr = 0; /* A couple addresses of generated code */ 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; /* VDBE cursor for the canonical data source */ int iIdxCur; /* 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 = -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 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; /* First of nPk registerss holding PRIMARY KEY value */ + i16 nPk = 1; /* Number of components of the PRIMARY KEY */ + int iKey; /* Memory cell holding row key of to be deleted */ + i16 nKey; /* Number of memory cells of 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 addrDelete = 0; /* Jump directly to the delete logic */ + int addrEphOpen = 0; /* Instruction to open the Ephermeral table */ + #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to delete from a view */ Trigger *pTrigger; /* List of table triggers, if required */ #endif @@ -293,15 +307,15 @@ if( rcauth==SQLITE_DENY ){ goto delete_from_cleanup; } assert(!isView || pTrigger); - /* Assign cursor number to the table and all its indices. + /* Assign cursor numbers to the table and all its indices. */ assert( pTabList->nSrc==1 ); iTabCur = pTabList->a[0].iCursor = pParse->nTab++; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ pParse->nTab++; } /* Start the view context */ @@ -363,132 +377,163 @@ assert( pIdx->pSchema==pTab->pSchema ); sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb); } }else #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ - if( !HasRowid(pTab) ){ - /* There is a WHERE clause on a WITHOUT ROWID table. - */ - Index *pPk; /* The PRIMARY KEY index on the table */ - int iPk; /* First of nPk memory cells holding PRIMARY KEY value */ - int iEph; /* Ephemeral table holding all primary key values */ - int iKey; /* Key value inserting into iEph */ - i16 nPk; /* Number of components of the PRIMARY KEY */ - - pPk = sqlite3PrimaryKeyIndex(pTab); - assert( pPk!=0 ); - nPk = pPk->nKeyCol; - iPk = pParse->nMem+1; - pParse->nMem += nPk; - iKey = ++pParse->nMem; - iEph = pParse->nTab++; - - sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); - sqlite3VdbeSetP4KeyInfo(pParse, pPk); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, 0, 0); - if( pWInfo==0 ) goto delete_from_cleanup; - for(i=0; iaiColumn[i],iPk+i); - } - sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey, - sqlite3IndexAffinityStr(v, pPk), P4_TRANSIENT); - sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, iKey); - if( db->flags & SQLITE_CountRows ){ - sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); - } - sqlite3WhereEnd(pWInfo); - - /* Open cursors for all indices of the table. - */ - sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, - iTabCur, &iDataCur, &iIdxCur); - - /* Loop over the primary keys to be deleted. */ - addr = sqlite3VdbeAddOp1(v, OP_Rewind, iEph); - sqlite3VdbeAddOp2(v, OP_RowKey, iEph, iPk); - - /* Delete the row */ - sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, - iPk, 0, 1, OE_Default, 0); - - /* End of the delete loop */ - sqlite3VdbeAddOp2(v, OP_Next, iEph, addr+1); - sqlite3VdbeJumpHere(v, addr); - - /* Close the cursors open on the table and its indexes. */ - assert( iDataCur>=iIdxCur ); - for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ - sqlite3VdbeAddOp1(v, OP_Close, iIdxCur+i); - } - }else{ - /* There is a WHERE clause on a rowid table. Run a loop that extracts - ** all rowids to be deleted into a RowSet. - */ - int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */ - int iRowid = ++pParse->nMem; /* Used for storing rowid values. */ - int regRowid; /* Actual register containing rowids */ - - /* Collect rowids of every row to be deleted. - */ - sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); - pWInfo = sqlite3WhereBegin( - pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK, 0 - ); - if( pWInfo==0 ) goto delete_from_cleanup; - regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iRowid, 0); - sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid); - if( db->flags & SQLITE_CountRows ){ - sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); - } - sqlite3WhereEnd(pWInfo); - - /* Delete every item whose key was written to the list during the - ** database scan. We have to delete items after the scan is complete - ** because deleting an item can change the scan order. */ - end = sqlite3VdbeMakeLabel(v); - - /* 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 ){ - sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, - &iDataCur, &iIdxCur); - assert( iDataCur==iTabCur ); - assert( iIdxCur==iDataCur+1 ); - } - - addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); - + { + if( HasRowid(pTab) ){ + /* For a rowid table, initialize the RowSet to an empty set */ + pPk = 0; + nPk = 1; + iRowSet = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); + }else{ + /* For a WITHOUT ROWID table, create an ephermeral 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. + */ + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, + WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK, + iTabCur+1); + if( pWInfo==0 ) goto delete_from_cleanup; + okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); + + /* Keep track of the number of rows to be deleted */ + if( db->flags & SQLITE_CountRows ){ + sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); + } + + /* Extract the rowid or primary key for the current row */ + if( pPk ){ + for(i=0; iaiColumn[i], iPk+i); + } + iKey = iPk; + nKey = nPk; + }else{ + iKey = pParse->nMem + 1; + iKey = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iKey, 0); + if( iKey>pParse->nMem ) pParse->nMem = iKey; + } + + if( okOnePass ){ + /* 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 = sqlite3DbMallocRaw(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); + 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), P4_TRANSIENT); + 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); + } + + /* End of the WHERE loop */ + sqlite3WhereEnd(pWInfo); + if( okOnePass ){ + /* Bypass the delete logic below if the WHERE loop found zero rows */ + addrBypass = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBypass); + sqlite3VdbeJumpHere(v, addrDelete); + } + + /* 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 ){ + sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen, + &iDataCur, &iIdxCur); + assert( pPk || iDataCur==iTabCur ); + assert( pPk || iIdxCur==iDataCur+1 ); + } + + /* 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 */ + assert( nKey==nPk ); /* OP_Found will use an unpacked key */ + if( aToOpen[iDataCur-iTabCur] ){ + assert( pPk!=0 ); + sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); + } + }else if( pPk ){ + addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); + sqlite3VdbeAddOp2(v, OP_RowKey, iEphCur, iKey); + assert( nKey==0 ); /* OP_Found will use a composite key */ + }else{ + addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey); + assert( nKey==1 ); + } + /* Delete the row */ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB); sqlite3VdbeChangeP5(v, OE_Abort); sqlite3MayAbort(pParse); }else #endif { int count = (pParse->nested==0); /* True to count changes */ sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, - iRowid, 1, count, OE_Default, 0); + iKey, nKey, count, OE_Default, okOnePass); } - - /* End of the delete loop */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); - sqlite3VdbeResolveLabel(v, end); - + + /* End of the loop over all rowids/primary-keys. */ + if( okOnePass ){ + sqlite3VdbeResolveLabel(v, addrBypass); + }else if( pPk ){ + sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); + sqlite3VdbeJumpHere(v, addrLoop); + }else{ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrLoop); + sqlite3VdbeJumpHere(v, addrLoop); + } + /* Close the cursors open on the table and its indexes. */ if( !isView && !IsVirtual(pTab) ){ - sqlite3VdbeAddOp1(v, OP_Close, iDataCur); + if( !pPk ) sqlite3VdbeAddOp1(v, OP_Close, iDataCur); for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ sqlite3VdbeAddOp1(v, OP_Close, iIdxCur + i); } } - } + } /* 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. */ @@ -508,10 +553,11 @@ delete_from_cleanup: sqlite3AuthContextPop(&sContext); sqlite3SrcListDelete(db, pTabList); sqlite3ExprDelete(db, pWhere); + sqlite3DbFree(db, aToOpen); return; } /* Make sure "isView" and other macros defined above are undefined. Otherwise ** thely may interfere with compilation of other functions in this file ** (or in another file, if this file becomes part of the amalgamation). */ @@ -574,10 +620,11 @@ /* 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 @@ -594,19 +641,23 @@ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, iCol, iOld+iCol+1); } } /* Invoke BEFORE DELETE trigger programs. */ + addrStart = sqlite3VdbeCurrentAddr(v); sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel ); - /* Seek the cursor to the row to be deleted again. It may be that - ** the BEFORE triggers coded above have already removed the row - ** being deleted. Do not attempt to delete the row a second time, and - ** do not fire AFTER triggers. */ - sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk); + /* 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 of already deleted the row that the cursor was + ** pointing to. + */ + if( addrStartdb, pTab->pSchema); v = sqlite3GetVdbe(pParse); assert( v!=0 ); if( iBase<0 ) iBase = pParse->nTab; - if( HasRowid(pTab) ){ - *piDataCur = iBase++; - sqlite3OpenTable(pParse, *piDataCur, iDb, pTab, op); + iDataCur = iBase++; + if( piDataCur ) *piDataCur = iDataCur; + if( HasRowid(pTab) && (aToOpen==0 || aToOpen[0]) ){ + sqlite3OpenTable(pParse, iDataCur, iDb, pTab, op); }else{ sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName); } - *piIdxCur = iBase; + if( piIdxCur ) *piIdxCur = iBase; for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ int iIdxCur = iBase++; assert( pIdx->pSchema==pTab->pSchema ); - if( pIdx->autoIndex==2 && !HasRowid(pTab) ) *piDataCur = iIdxCur; - sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb); - sqlite3VdbeSetP4KeyInfo(pParse, pIdx); - VdbeComment((v, "%s", pIdx->zName)); + if( pIdx->autoIndex==2 && !HasRowid(pTab) && piDataCur ){ + *piDataCur = iIdxCur; + } + if( aToOpen==0 || aToOpen[i+1] ){ + sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb); + sqlite3VdbeSetP4KeyInfo(pParse, pIdx); + VdbeComment((v, "%s", pIdx->zName)); + } } if( iBase>pParse->nTab ) pParse->nTab = iBase; return i; } Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -1889,11 +1889,11 @@ addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Stop if out of errors */ sqlite3VdbeAddOp2(v, OP_Halt, 0, 0); sqlite3VdbeJumpHere(v, addr); sqlite3ExprCacheClear(pParse); sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, - 1, &iDataCur, &iIdxCur); + 1, 0, &iDataCur, &iIdxCur); sqlite3VdbeAddOp2(v, OP_Integer, 0, 7); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */ } pParse->nMem = MAX(pParse->nMem, 8+j); Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -2940,11 +2940,11 @@ void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*); int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*); void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int, u8,u8,int,int*); void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int); -int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, int*, int*); +int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, u8*, int*, int*); void sqlite3BeginWriteOperation(Parse*, int, int); void sqlite3MultiWrite(Parse*); void sqlite3MayAbort(Parse*); void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8); void sqlite3UniqueConstraint(Parse*, int, Index*); Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -99,22 +99,23 @@ WhereInfo *pWInfo; /* Information about the WHERE clause */ Vdbe *v; /* The virtual database engine */ Index *pIdx; /* For looping over indices */ Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */ int nIdx; /* Number of indices that need updating */ + int iBaseCur; /* Base cursor number */ int iDataCur; /* Cursor for the canonical data btree */ int iIdxCur; /* Cursor for the first index */ sqlite3 *db; /* The database structure */ int *aRegIdx = 0; /* One register assigned to each index to be updated */ int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ + u8 *aToOpen; /* 1 for tables and indices to be opened */ u8 chngPk; /* PRIMARY KEY changed in a WITHOUT ROWID table */ u8 chngRowid; /* Rowid changed in a normal table */ u8 chngKey; /* Either chngPk or chngRowid */ Expr *pRowidExpr = 0; /* Expression defining the new record number */ - int openAll = 0; /* True if all indices need to be opened */ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ int okOnePass; /* True for one-pass algorithm without the FIFO */ int hasFK; /* True if foreign key processing is required */ @@ -174,29 +175,37 @@ goto update_cleanup; } if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ goto update_cleanup; } - aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol ); - if( aXRef==0 ) goto update_cleanup; - for(i=0; inCol; i++) aXRef[i] = -1; /* Allocate a cursors for the main database table and for all indices. ** The index cursors might not be used, but if they are used they ** need to occur right after the database cursor. So go ahead and ** allocate enough space, just in case. */ - pTabList->a[0].iCursor = iDataCur = pParse->nTab++; + pTabList->a[0].iCursor = iBaseCur = iDataCur = pParse->nTab++; iIdxCur = iDataCur+1; pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ if( pIdx->autoIndex==2 && pPk!=0 ){ iDataCur = pParse->nTab; pTabList->a[0].iCursor = iDataCur; } pParse->nTab++; } + + /* Allocate space for aXRef[], aRegIdx[], and aToOpen[]. + ** Initialize aXRef[] and aToOpen[] to their default values. + */ + aXRef = sqlite3DbMallocRaw(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 ); + if( aXRef==0 ) goto update_cleanup; + aRegIdx = aXRef+pTab->nCol; + aToOpen = (u8*)(aRegIdx+nIdx); + memset(aToOpen, 1, nIdx+1); + aToOpen[nIdx+1] = 0; + for(i=0; inCol; i++) aXRef[i] = -1; /* Initialize the name-context */ memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; sNC.pSrcList = pTabList; @@ -254,19 +263,14 @@ assert( chngPk==0 || chngPk==1 ); chngKey = chngRowid + chngPk; hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey); - /* Allocate memory for the array aRegIdx[]. There is one entry in the - ** array for each index associated with table being updated. Fill in - ** the value with a register number for indices that are to be used - ** and with zero for unused indices. + /* There is one entry in the aRegIdx[] array for each index on the table + ** being updated. Fill in aRegIdx[] with a register number that will hold + ** the key for accessing each index. */ - if( nIdx>0 ){ - aRegIdx = sqlite3DbMallocRaw(db, sizeof(Index*) * nIdx ); - if( aRegIdx==0 ) goto update_cleanup; - } for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int reg; if( chngKey || hasFK || pIdx->pPartIdxWhere || pIdx==pPk ){ reg = ++pParse->nMem; }else{ @@ -276,10 +280,11 @@ reg = ++pParse->nMem; break; } } } + if( reg==0 ) aToOpen[j+1] = 0; aRegIdx[j] = reg; } /* Begin generating code. */ v = sqlite3GetVdbe(pParse); @@ -399,46 +404,34 @@ ** Open every index that needs updating. Note that if any ** index could potentially invoke a REPLACE conflict resolution ** action, then we need to open all indices because we might need ** to be deleting some records. */ - if( !okOnePass && HasRowid(pTab) ){ - sqlite3OpenTable(pParse, iDataCur, iDb, pTab, OP_OpenWrite); - } - sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName); if( onError==OE_Replace ){ - openAll = 1; + memset(aToOpen, 1, nIdx+1); }else{ - openAll = 0; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->onError==OE_Replace ){ - openAll = 1; + memset(aToOpen, 1, nIdx+1); break; } } } - for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ - int iThisCur = iIdxCur+i; - assert( aRegIdx ); - if( (openAll || aRegIdx[i]>0) - && iThisCur!=aiCurOnePass[1] - ){ - assert( iThisCur!=aiCurOnePass[0] ); - sqlite3VdbeAddOp3(v, OP_OpenWrite, iThisCur, pIdx->tnum, iDb); - sqlite3VdbeSetP4KeyInfo(pParse, pIdx); - assert( pParse->nTab>iThisCur ); - VdbeComment((v, "%s", pIdx->zName)); - if( okOnePass && pPk && iThisCur==iDataCur ){ - sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, - regKey, nKey); - } - } - } + if( okOnePass ){ + if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0; + if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0; + } + sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iBaseCur, aToOpen, + 0, 0); } /* Top of the update loop */ if( okOnePass ){ + if( aToOpen[iDataCur-iBaseCur] ){ + assert( pPk!=0 ); + sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey); + } labelContinue = labelBreak; sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak); }else if( pPk ){ labelContinue = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); @@ -627,11 +620,11 @@ sqlite3VdbeResolveLabel(v, labelBreak); /* Close all tables */ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ assert( aRegIdx ); - if( openAll || aRegIdx[i]>0 ){ + if( aToOpen[i+1] ){ sqlite3VdbeAddOp2(v, OP_Close, iIdxCur+i, 0); } } if( iDataCur