From: drh Date: Fri, 8 Nov 2013 15:19:46 +0000 (+0000) Subject: Performance improvements: X-Git-Tag: version-3.8.2~127 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fc8d4f96b4ebb4cd7adffa128adb7b9198e3434a;p=thirdparty%2Fsqlite.git Performance improvements: Avoid unnecessary seeks when doing a single-row UPDATE on a WITHOUT ROWID table. FossilOrigin-Name: 6f187a0fb1b09ebc4732c4afbf3c813f82e069f1 --- diff --git a/manifest b/manifest index be690b0766..e7d399acd1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\schange\sto\sdrop\sthe\smutex\son\sthe\smultiplexor\sbefore\sentering\sthe\sxRead\nVFS\scall,\sin\sorder\sto\senhance\sparallelizability. -D 2013-11-08T12:14:50.705 +C Performance\simprovements:\nAvoid\sunnecessary\sseeks\swhen\sdoing\sa\ssingle-row\sUPDATE\son\sa\sWITHOUT\sROWID\ntable. +D 2013-11-08T15:19:46.383 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in d12e4455cf7a36e42d3949876c1c3b88ff70867a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -223,7 +223,7 @@ F src/shell.c 03d8d9b4052430343ff30d646334621f980f1202 F src/sqlite.h.in a8cad179541b8d171fed425a737084702ef462ef F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h 360c8a484065f6b52ecdd5ef6766429e7aa552dd +F src/sqliteInt.h b4311956d26be8036f5feb2f1c904c313979ea17 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -275,11 +275,11 @@ F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c ec4c1a62b890bf1dbcdb966399e140b904c700a4 F src/trigger.c 53d6b5d50b3b23d4fcd0a36504feb5cff9aed716 -F src/update.c 79f10ddcb078778193b27a9e050d14741b93148a +F src/update.c 95a640c56d71ea9d58be66eab863d502701d092b F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269 F src/util.c 2fa6c821d28bbdbeec1b2a7b091a281c9ef8f918 F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179 -F src/vdbe.c ff0170cd3c5fea68386818cbd19a23bbfc711364 +F src/vdbe.c f635690f3f2253ac8db7d505a100383a9a555362 F src/vdbe.h 8d5a7351024d80374fc0acdbbe3cfe65c51ba8b6 F src/vdbeInt.h f2fa3ceccceeb757773921fb08af7c6e9f3caa1c F src/vdbeapi.c 93a22a9ba2abe292d5c2cf304d7eb2e894dde0ed @@ -292,7 +292,7 @@ F src/vtab.c 5a423b042eb1402ef77697d03d6a67378d97bc8d F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c e9e593d5bb798c3e67fc3893dfe7055c9e7d8d74 -F src/where.c 30462cf04157b872b4443724739ef617b1535686 +F src/where.c 1b5780b645dfba94ea63f2fb845a8bbfeada4162 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 @@ -1135,8 +1135,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 74e3ee2ee6ea89af2c12dd0bce248467fd0f1310 a00d2ed49c9f53263cd76ad41dad9e35e646ebb5 -R 3835fd3c0df78c22220952d9100c6a09 -T +closed a00d2ed49c9f53263cd76ad41dad9e35e646ebb5 +P 3c566e41e4c9c66960cc5a3ddee8556835237999 +R cbe50e39428754e7aa6f05505b294390 U drh -Z 74a2387ae02d248fbf85568c38a09fbd +Z 2b1321d582e3f35ab93369db1373861d diff --git a/manifest.uuid b/manifest.uuid index 5bc804df30..3cdcec40ae 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3c566e41e4c9c66960cc5a3ddee8556835237999 \ No newline at end of file +6f187a0fb1b09ebc4732c4afbf3c813f82e069f1 \ No newline at end of file diff --git a/src/sqliteInt.h b/src/sqliteInt.h index ea0cde7767..934ba3c965 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2876,7 +2876,7 @@ int sqlite3WhereIsDistinct(WhereInfo*); int sqlite3WhereIsOrdered(WhereInfo*); int sqlite3WhereContinueLabel(WhereInfo*); int sqlite3WhereBreakLabel(WhereInfo*); -int sqlite3WhereOkOnePass(WhereInfo*); +int sqlite3WhereOkOnePass(WhereInfo*, int*); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8); void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); void sqlite3ExprCodeMove(Parse*, int, int, int); diff --git a/src/update.c b/src/update.c index 1bc4fed921..bb4a3aba51 100644 --- a/src/update.c +++ b/src/update.c @@ -129,6 +129,7 @@ void sqlite3Update( int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */ int iEph = 0; /* Ephemeral table holding all primary key values */ int nKey = 0; /* Number of elements in regKey for WITHOUT ROWID */ + int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */ /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ @@ -336,10 +337,10 @@ void sqlite3Update( if( HasRowid(pTab) ){ sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid); pWInfo = sqlite3WhereBegin( - pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0 + pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, iIdxCur ); if( pWInfo==0 ) goto update_cleanup; - okOnePass = sqlite3WhereOkOnePass(pWInfo); + okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); /* Remember the rowid of every item to be updated. */ @@ -366,9 +367,9 @@ void sqlite3Update( addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk); sqlite3VdbeSetP4KeyInfo(pParse, pPk); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, - WHERE_ONEPASS_DESIRED, 0); + WHERE_ONEPASS_DESIRED, iIdxCur); if( pWInfo==0 ) goto update_cleanup; - okOnePass = sqlite3WhereOkOnePass(pWInfo); + okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass); for(i=0; iaiColumn[i], iPk+i); @@ -392,6 +393,7 @@ void sqlite3Update( sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); } + labelBreak = sqlite3VdbeMakeLabel(v); if( !isView ){ /* ** Open every index that needs updating. Note that if any @@ -415,24 +417,28 @@ void sqlite3Update( } } for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ + int iThisCur = iIdxCur+i; assert( aRegIdx ); - if( openAll || aRegIdx[i]>0 ){ - sqlite3VdbeAddOp3(v, OP_OpenWrite, iIdxCur+i, pIdx->tnum, iDb); + if( (openAll || aRegIdx[i]>0) + && iThisCur!=aiCurOnePass[0] + && iThisCur!=aiCurOnePass[1] + ){ + sqlite3VdbeAddOp3(v, OP_OpenWrite, iThisCur, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); - assert( pParse->nTab>iIdxCur+i ); + assert( pParse->nTab>iThisCur ); VdbeComment((v, "%s", pIdx->zName)); + if( okOnePass && pPk && iThisCur==iDataCur ){ + sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, + regKey, nKey); + } } } } /* Top of the update loop */ - labelBreak = sqlite3VdbeMakeLabel(v); if( okOnePass ){ labelContinue = labelBreak; sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak); - if( pPk ){ - sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey); - } }else if( pPk ){ labelContinue = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); @@ -624,7 +630,7 @@ void sqlite3Update( for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ assert( aRegIdx ); if( openAll || aRegIdx[i]>0 ){ - sqlite3VdbeAddOp2(v, OP_Close, iIdxCur, 0); + sqlite3VdbeAddOp2(v, OP_Close, iIdxCur+i, 0); } } if( iDataCurnQueryLoop outside the WHERE loop */ + int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */ WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ WhereClause sWC; /* Decomposition of the WHERE clause */ WhereLevel a[1]; /* Information about each nest loop in WHERE */ @@ -501,8 +502,19 @@ int sqlite3WhereBreakLabel(WhereInfo *pWInfo){ ** Return TRUE if an UPDATE or DELETE statement can operate directly on ** the rowids returned by a WHERE clause. Return FALSE if doing an ** UPDATE or DELETE might change subsequent WHERE clause results. +** +** If the ONEPASS optimization is used (if this routine returns true) +** then also write the indices of open cursors used by ONEPASS +** into aiCur[0] and aiCur[1]. iaCur[0] gets the cursor of the data +** table and iaCur[1] gets the cursor used by an auxiliary index. +** Either value may be -1, indicating that cursor is not used. +** Any cursors returned will have been opened for writing. +** +** aiCur[0] and aiCur[1] both get -1 if the where-clause logic is +** unable to use the ONEPASS optimization. */ -int sqlite3WhereOkOnePass(WhereInfo *pWInfo){ +int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){ + memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2); return pWInfo->okOnePass; } @@ -3152,7 +3164,7 @@ static Bitmask codeOneLoopStart( bRev = (pWInfo->revMask>>iLevel)&1; omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 && (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)==0; - VdbeNoopComment((v, "Begin Join Loop %d", iLevel)); + VdbeNoopComment((v, "Begin WHERE-Loop %d: %s", iLevel,pTabItem->pTab->zName)); /* Create labels for the "break" and "continue" instructions ** for the current loop. Jump to addrBrk to break out of a loop. @@ -5694,6 +5706,14 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ ** if the WHERE_GROUPBY flag is set in wctrlFlags) of a SELECT statement ** if there is one. If there is no ORDER BY clause or if this routine ** is called from an UPDATE or DELETE statement, then pOrderBy is NULL. +** +** The iIdxCur parameter is the cursor number of an index. If +** WHERE_ONETABLE_ONLY is set, iIdxCur is the cursor number of an index +** to use for OR clause processing. The WHERE clause should use this +** specific cursor. If WHERE_ONEPASS_DESIRED is set, then iIdxCur is +** the first cursor in an array of cursors for all indices. iIdxCur should +** be used to compute the appropriate cursor depending on which index is +** used. */ WhereInfo *sqlite3WhereBegin( Parse *pParse, /* The parser context */ @@ -5759,6 +5779,7 @@ WhereInfo *sqlite3WhereBegin( pWInfo = 0; goto whereBeginError; } + pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1; pWInfo->nLevel = nTabList; pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; @@ -6022,8 +6043,13 @@ WhereInfo *sqlite3WhereBegin( #endif if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){ - int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead; + int op = OP_OpenRead; + if( pWInfo->okOnePass ){ + op = OP_OpenWrite; + pWInfo->aiCurOnePass[0] = pTabItem->iCursor; + }; sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op); + assert( pTabItem->iCursor==pLevel->iTabCur ); testcase( !pWInfo->okOnePass && pTab->nCol==BMS-1 ); testcase( !pWInfo->okOnePass && pTab->nCol==BMS ); if( !pWInfo->okOnePass && pTab->nColwsFlags & WHERE_INDEXED ){ Index *pIx = pLoop->u.btree.pIndex; - /* FIXME: As an optimization use pTabItem->iCursor if WHERE_IDX_ONLY */ - int iIndexCur = pLevel->iIdxCur = iIdxCur ? iIdxCur : pParse->nTab++; + int iIndexCur; + int op = OP_OpenRead; + if( pWInfo->okOnePass && iIdxCur ){ + Index *pJ = pTabItem->pTab->pIndex; + iIndexCur = iIdxCur; + assert( wctrlFlags & WHERE_ONEPASS_DESIRED ); + while( ALWAYS(pJ) && pJ!=pIx ){ + iIndexCur++; + pJ = pJ->pNext; + } + op = OP_OpenWrite; + pWInfo->aiCurOnePass[1] = iIndexCur; + }else if( iIdxCur && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ){ + iIndexCur = iIdxCur; + }else{ + iIndexCur = pParse->nTab++; + } + pLevel->iIdxCur = iIndexCur; assert( pIx->pSchema==pTab->pSchema ); assert( iIndexCur>=0 ); - sqlite3VdbeAddOp3(v, OP_OpenRead, iIndexCur, pIx->tnum, iDb); + sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIx); VdbeComment((v, "%s", pIx->zName)); } @@ -6139,6 +6181,8 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ } sqlite3VdbeJumpHere(v, addr); } + VdbeNoopComment((v, "End WHERE-Loop %d: %s", i, + pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); } /* The "break" point is here, just past the end of the outer loop. @@ -6146,8 +6190,6 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ */ sqlite3VdbeResolveLabel(v, pWInfo->iBreak); - /* Close all of the cursors that were opened by sqlite3WhereBegin. - */ assert( pWInfo->nLevel<=pTabList->nSrc ); for(i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++){ Index *pIdx = 0; @@ -6155,6 +6197,12 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ Table *pTab = pTabItem->pTab; assert( pTab!=0 ); pLoop = pLevel->pWLoop; + + /* Close all of the cursors that were opened by sqlite3WhereBegin. + ** Except, do not close cursors that will be reused by the OR optimization + ** (WHERE_OMIT_OPEN_CLOSE). And do not close the OP_OpenWrite cursors + ** created for the ONEPASS optimization. + */ if( (pTab->tabFlags & TF_Ephemeral)==0 && pTab->pSelect==0 && (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 @@ -6163,7 +6211,10 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){ sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); } - if( (ws & WHERE_INDEXED)!=0 && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 ){ + if( (ws & WHERE_INDEXED)!=0 + && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 + && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1] + ){ sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); } }