From: drh Date: Sat, 16 Nov 2013 22:48:52 +0000 (+0000) Subject: Combine the rowid and WITHOUT ROWID paths for DELETE into a single path. X-Git-Tag: version-3.8.2~81^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9612947260c61cb2497a8bfa174f59a2eb6d63d0;p=thirdparty%2Fsqlite.git Combine the rowid and WITHOUT ROWID paths for DELETE into a single path. FossilOrigin-Name: c4734b881a64a9d21d03a14e901785797577fbd8 --- diff --git a/manifest b/manifest index 6c93f9afcb..dd8429d568 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sone-pass\soptimization\sis\snow\sworking\sfor\sDELETE\son\sWITHOUT\sROWID\stables. -D 2013-11-16T20:45:01.087 +C Combine\sthe\srowid\sand\sWITHOUT\sROWID\spaths\sfor\sDELETE\sinto\sa\ssingle\spath. +D 2013-11-16T22:48:52.173 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 8a07bebafbfda0eb67728f4bd15a36201662d1a1 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -174,7 +174,7 @@ F src/callback.c f99a8957ba2adf369645fac0db09ad8adcf1caa2 F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c F src/date.c 593c744b2623971e45affd0bde347631bdfa4625 -F src/delete.c 35750d35fe9f174ccb98bae0d6627dcf83a7965a +F src/delete.c 1bcc9d7f2e48cf9043a44bdbd333c38c2ef6676a F src/expr.c 1a295d8b0a2ba08919ad9300ebf7b67988ff4030 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 78364daed38e26269c53ddb94c515bceac1063c6 @@ -1140,7 +1140,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 8f479a72758ab6fedb171ada612b1963143c32fa -R b6e41a6a6d551dfa6da0e7b0e42124eb +P e4d220a381388f900a95d1b656a82f14c837f92e +R d0df7aac2a46f76fbd08b30c4f269d2a U drh -Z f0edfafd89abfe9d193a6a7758aa54ff +Z 2d947a1a8ff0ceb82e3a6fad03406bbd diff --git a/manifest.uuid b/manifest.uuid index 1a4b2e1489..e3dbc37284 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e4d220a381388f900a95d1b656a82f14c837f92e \ No newline at end of file +c4734b881a64a9d21d03a14e901785797577fbd8 \ No newline at end of file diff --git a/src/delete.c b/src/delete.c index 30b415aa35..35010a483a 100644 --- a/src/delete.c +++ b/src/delete.c @@ -227,7 +227,6 @@ void sqlite3DeleteFrom( 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 */ @@ -244,7 +243,18 @@ void sqlite3DeleteFrom( 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 */ @@ -369,146 +379,121 @@ void sqlite3DeleteFrom( } }else #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ - if( !HasRowid(pTab) ){ - /* There is a WHERE clause on a WITHOUT ROWID table. + { + 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. */ - 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++; - - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); - sqlite3VdbeSetP4KeyInfo(pParse, pPk); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, iTabCur+1); if( pWInfo==0 ) goto delete_from_cleanup; okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); - for(i=0; iaiColumn[i],iPk+i); - } + + /* Keep track of the number of rows to be deleted */ if( db->flags & SQLITE_CountRows ){ sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); } - if( okOnePass ){ - aToOpen = sqlite3DbMallocRaw(db, nIdx+2); - if( aToOpen==0 ) 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; - sqlite3VdbeChangeToNoop(v, addr); - addr = sqlite3VdbeAddOp0(v, OP_Goto); + + /* Extract the rowid or primary key for the current row */ + if( pPk ){ + for(i=0; iaiColumn[i], iPk+i); + } + iKey = iPk; + nKey = nPk; }else{ - sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey, - sqlite3IndexAffinityStr(v, pPk), P4_TRANSIENT); - sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, iKey); - } - sqlite3WhereEnd(pWInfo); - if( okOnePass ){ - sqlite3VdbeAddOp0(v, OP_Halt); - sqlite3VdbeJumpHere(v, addr); - } - - /* Open cursors for all indices of the table. - */ - sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen, - &iDataCur, &iIdxCur); - - /* Loop over the primary keys to be deleted. */ - if( !okOnePass ){ - addr = sqlite3VdbeAddOp1(v, OP_Rewind, iEph); - sqlite3VdbeAddOp2(v, OP_RowKey, iEph, iPk); - } - - /* Delete the row */ - sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, - iPk, nPk*okOnePass, 1, OE_Default, okOnePass); - - /* End of the delete loop */ - if( !okOnePass ){ - 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 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|WHERE_ONEPASS_DESIRED, iTabCur+1 - ); - if( pWInfo==0 ) goto delete_from_cleanup; - okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); - if( db->flags & SQLITE_CountRows ){ - sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); + iKey = pParse->nMem + 1; + iKey = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iKey, 0); + if( iKey>pParse->nMem ) pParse->nMem = iKey; } - regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, - pParse->nMem+1, 0); - if( regRowid>pParse->nMem ) pParse->nMem = regRowid; + 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 ) 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; - addr = sqlite3VdbeAddOp0(v, OP_Goto); + 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{ - sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid); + /* 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 ){ - sqlite3VdbeAddOp0(v, OP_Halt); - sqlite3VdbeJumpHere(v, addr); + /* Bypass the delete logic below if the WHERE loop found zero rows */ + addrBypass = sqlite3VdbeAddOp0(v, OP_Goto); + sqlite3VdbeJumpHere(v, addrDelete); } - - /* 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. */ + ** triggers. + */ if( !isView ){ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen, &iDataCur, &iIdxCur); - assert( iDataCur==iTabCur ); - assert( iIdxCur==iDataCur+1 ); - } - - if( !okOnePass ){ - addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, regRowid); + 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 */ + }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, regRowid, pVTab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB); sqlite3VdbeChangeP5(v, OE_Abort); sqlite3MayAbort(pParse); }else @@ -516,23 +501,28 @@ void sqlite3DeleteFrom( { int count = (pParse->nested==0); /* True to count changes */ sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, - regRowid, 1, count, OE_Default, okOnePass); - } - - /* End of the delete loop */ - if( !okOnePass ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); + iKey, nKey, count, OE_Default, okOnePass); } - sqlite3VdbeResolveLabel(v, end); - + + /* End of the loop over all rowids/primary-keys. */ + if( okOnePass ){ + sqlite3VdbeJumpHere(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