]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Performance improvements:
authordrh <drh@noemail.net>
Fri, 8 Nov 2013 15:19:46 +0000 (15:19 +0000)
committerdrh <drh@noemail.net>
Fri, 8 Nov 2013 15:19:46 +0000 (15:19 +0000)
Avoid unnecessary seeks when doing a single-row UPDATE on a WITHOUT ROWID
table.

FossilOrigin-Name: 6f187a0fb1b09ebc4732c4afbf3c813f82e069f1

manifest
manifest.uuid
src/sqliteInt.h
src/update.c
src/vdbe.c
src/where.c

index be690b076640598aa72ce825d0fc6dfcf12d37d8..e7d399acd1a4ef804781a60b18fa2c63f2768a8e 100644 (file)
--- 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
index 5bc804df30aa79f6e458912573a7afbf4b18705c..3cdcec40aeba2ef7541633f6d8a85ce72330fe4c 100644 (file)
@@ -1 +1 @@
-3c566e41e4c9c66960cc5a3ddee8556835237999
\ No newline at end of file
+6f187a0fb1b09ebc4732c4afbf3c813f82e069f1
\ No newline at end of file
index ea0cde7767742a5a7ac7e8c3f1ac27807f4bd3a6..934ba3c965f3b020afc7db76b27652e4f910df24 100644 (file)
@@ -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);
index 1bc4fed92172423bba6b39dfaddf5bb77ee55fb9..bb4a3aba511a23518d50276680d1e6a6c31800db 100644 (file)
@@ -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; i<nPk; i++){
       sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[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( iDataCur<iIdxCur ) sqlite3VdbeAddOp2(v, OP_Close, iDataCur, 0);
index 97b35196d3dd261fcf1db35fd8c8897bdd3cd955..23aa47079022f3b1c8f84f82cd842be4bcb7ce71 100644 (file)
@@ -2182,6 +2182,7 @@ case OP_IfNot: {            /* jump, in1 */
 }
 
 /* Opcode: IsNull P1 P2 * * *
+** Synopsis:  if r[P1]==NULL goto P2
 **
 ** Jump to P2 if the value in register P1 is NULL.
 */
@@ -2194,6 +2195,7 @@ case OP_IsNull: {            /* same as TK_ISNULL, jump, in1 */
 }
 
 /* Opcode: NotNull P1 P2 * * *
+** Synopsis: if r[P1]!=NULL goto P2
 **
 ** Jump to P2 if the value in register P1 is not NULL.  
 */
index c26465e97076fd23054d9748ddf4cef72e74c38d..0a68a9c075e22ece3003fc9197eeee8e34c7ad42 100644 (file)
@@ -409,6 +409,7 @@ struct WhereInfo {
   int iContinue;            /* Jump here to continue with next record */
   int iBreak;               /* Jump here to break out of the loop */
   int savedNQueryLoop;      /* pParse->nQueryLoop 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->nCol<BMS && HasRowid(pTab) ){
@@ -6039,11 +6065,27 @@ WhereInfo *sqlite3WhereBegin(
     }
     if( pLoop->wsFlags & 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; i<pWInfo->nLevel; 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);
       }
     }