]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
First attempt to get ORDER BY optimization working in NGQP.
authordrh <drh@noemail.net>
Tue, 14 May 2013 15:31:07 +0000 (15:31 +0000)
committerdrh <drh@noemail.net>
Tue, 14 May 2013 15:31:07 +0000 (15:31 +0000)
FossilOrigin-Name: 9fe20292558bb9422de91e35648cb834cbf3b306

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

index 53d49f8c0caee574ccdb9403da99b2817e8c614d..316d75d659911e6df119cba2bd1d1961289dc180 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Minor\sfixes\sto\sthe\sOR-clause\sprocessing\sin\sthe\sNGQP.
-D 2013-05-11T00:06:23.252
+C First\sattempt\sto\sget\sORDER\sBY\soptimization\sworking\sin\sNGQP.
+D 2013-05-14T15:31:07.121
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in ce81671efd6223d19d4c8c6b88ac2c4134427111
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -195,7 +195,7 @@ F src/shell.c 2109d54f67c815a100abd7dc6a6e25eddb3b97eb
 F src/sqlite.h.in 5a5a22a9b192d81a9e5dee00274e3a0484c4afb1
 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
 F src/sqlite3ext.h d936f797812c28b81b26ed18345baf8db28a21a5
-F src/sqliteInt.h fab5580c75a16dced398bdd249c0409c9441b466
+F src/sqliteInt.h b9b6a2e97254fb50b6977ec55ad0f4e4ce0510d2
 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
 F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -264,7 +264,7 @@ F src/vtab.c b05e5f1f4902461ba9f5fc49bb7eb7c3a0741a83
 F src/wal.c 436bfceb141b9423c45119e68e444358ee0ed35d
 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
 F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73
-F src/where.c 049e7011b90717768eed37cdbe912d85f66a395b
+F src/where.c 51d935c6593748ed004006ee0f2147a860d56e90
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
@@ -1062,7 +1062,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
-P e17003fcfec0c0b524b1b9ff8e15e7ee83efa571
-R eb646fd2ea7876f6a2dea69031e80ca3
+P d6946f33c7851aa7efb04b93ac2ae1ac50c26eec
+R fe984d1689a56a08a31662e67855ab37
 U drh
-Z cf78dc58c7e86082569e8dd0f46801e5
+Z 13c634b8e2044390ac483fb2d5137194
index be800b411f03485e0445b2b889c5feca47c87a5e..7326bbb49926d0e590d095e5dabf9de4a480b535 100644 (file)
@@ -1 +1 @@
-d6946f33c7851aa7efb04b93ac2ae1ac50c26eec
\ No newline at end of file
+9fe20292558bb9422de91e35648cb834cbf3b306
\ No newline at end of file
index 45ae796c44234f3e2e1eb7a0a12ed23747e75f38..3ba0a3597b52ace1db3f0375d7bd3d5b6ba9c51b 100644 (file)
@@ -2045,7 +2045,7 @@ struct WhereLevel {
 #define WHERE_FORCE_TABLE      0x0020 /* Do not use an index-only search */
 #define WHERE_ONETABLE_ONLY    0x0040 /* Only code the 1st table in pTabList */
 #define WHERE_AND_ONLY         0x0080 /* Don't use indices for OR terms */
-#define WHREE_GROUPBY          0x0100 /* pOrderBy is really a GROUP BY */
+#define WHERE_GROUPBY          0x0100 /* pOrderBy is really a GROUP BY */
 
 /*
 ** The WHERE clause processing routine has two halves.  The
index d0a3fce772946b870d0c9626e255005192e94e33..2eff05ab398e7df94b2738d1737cac1a2e7039f7 100644 (file)
@@ -84,6 +84,7 @@ struct WhereLoop {
 */
 struct WherePath {
   Bitmask maskLoop;     /* Bitmask of all WhereLoop objects in this path */
+  Bitmask revLoop;      /* aLoop[]s that should be reversed for ORDER BY */
   double nRow;          /* Estimated number of rows generated by this path */
   double rCost;         /* Total cost of this path */
   u8 isOrdered;         /* True if this path satisfies ORDER BY */
@@ -5078,8 +5079,13 @@ static void whereLoopPrint(WhereLoop *p, SrcList *pTabList){
                      pItem->zAlias ? pItem->zAlias : pTab->zName);
   if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
     if( p->u.btree.pIndex ){
-      sqlite3DebugPrintf(".%-12s %2d",
-                         p->u.btree.pIndex->zName, p->u.btree.nEq);
+      const char *zName = p->u.btree.pIndex->zName;
+      if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){
+        int i = sqlite3Strlen30(zName) - 1;
+        while( zName[i]!='_' ) i--;
+        zName += i;
+      }
+      sqlite3DebugPrintf(".%-12s %2d", zName, p->u.btree.nEq);
     }else{
       sqlite3DebugPrintf("%16s","");
     }
@@ -5734,25 +5740,139 @@ whereLoopAddAll_end:
 }
 
 /*
-** Examine a WherePath to see if it outputs rows in the requested ORDER BY
-** (or GROUP BY) without requiring a separate source operation.  Return 1
-** if it does and 0 if it does not and -1 if we cannot tell.
+** Examine a WherePath (with the addition of the extra WhereLoop of the 4th
+** parameters) to see if it outputs rows in the requested ORDER BY
+** (or GROUP BY) without requiring a separate source operation.  Return:
+** 
+**    0:  ORDER BY is not satisfied.  Sorting required
+**    1:  ORDER BY is satisfied.      Omit sorting
+**   -1:  Unknown at this time
+**
 */
 static int wherePathSatisfiesOrderBy(
   WhereInfo *pWInfo,    /* The WHERE clause */
   WherePath *pPath,     /* The WherePath to check */
   int nLoop,            /* Number of entries in pPath->aLoop[] */
-  WhereLoop *pLoop      /* Add this WhereLoop to the end of pPath->aLoop[] */
+  WhereLoop *pLast,     /* Add this WhereLoop to the end of pPath->aLoop[] */
+  Bitmask *pRevMask     /* Mask of WhereLoops to run in reverse order */
 ){
-  if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
-    return nLoop==0 && pLoop->u.vtab.isOrdered;
-  }else{
-    /* TBD: Check to see if pFrom + pWLoop satisfies the ORDER BY.
-    **  (1)  If yes:   set isOrderedValid and isOrdered to 1.
-    **  (2)  If no:    set isOrderedValid to 1 and isOrdered to 0.
-    **  (3)  unknown:  no-op */
-    return 0;
+  u8 revSet;
+  u8 rev;
+  u8 isUnique;
+  u8 requireUnique = 0;
+  u16 nColumn;
+  u16 nOrderBy;
+  int i, j;
+  int nUsed = 0;
+  int iCur;
+  int iColumn;
+  WhereLoop *pLoop;
+  ExprList *pOrderBy = pWInfo->pOrderBy;
+  Expr *pOBExpr;
+  CollSeq *pColl;
+  Index *pIndex;
+  sqlite3 *db = pWInfo->pParse->db;
+  Bitmask revMask = 0;
+
+  /*
+  ** We say the WhereLoop is "one-row" if all of the following are true:
+  **  (a) All index columns match with WHERE_COLUMN_EQ.
+  **  (b) The index is unique
+  **
+  ** General rules:  (not an algorithm!)
+  **
+  **  (1) If the current WhereLoop is one-row, then match over any and all
+  **      ORDER BY terms for the current WhereLoop and proceed to the next
+  **      WhereLoop.
+  **
+  **  (2) If the current WhereLoop is not one-row, then all subsequent
+  **      WhereLoops must be one-row.
+  **
+  **  (3) Optionally match any ORDER BY terms against the first nEq columns
+  **      of the index.
+  **
+  **  (4) Index columns past nEq must match ORDER BY terms one-for-one.
+  */
+
+  assert( pOrderBy!=0 );
+
+  /* Sortability of virtual tables is determined by the xBestIndex method
+  ** of the virtual table itself */
+  if( pLast->wsFlags & WHERE_VIRTUALTABLE ){
+    assert( nLoop==0 );
+    return pLast->u.vtab.isOrdered;
   }
+
+  /* Sorting is always required if any term of the ORDER BY is not a 
+  ** column reference */
+  nOrderBy = pOrderBy->nExpr;
+  for(i=0; i<nOrderBy; i++){
+    pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[nUsed].pExpr);
+    if( pOBExpr->op!=TK_COLUMN ) return 0;
+  }
+    
+  for(i=0; i<=nLoop && nUsed<nOrderBy; i++){
+    pLoop = i<nLoop ? pPath->aLoop[i] : pLast;
+    assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 );
+    isUnique = 1;
+    if( pLoop->wsFlags & WHERE_IPK ){
+      if( (pLoop->wsFlags & WHERE_COLUMN_EQ)!=0 ) isUnique = 0;
+      pIndex = 0;
+      nColumn = 1;
+    }else if( pLoop->u.btree.pIndex==0 ){
+      return 0;
+    }else{
+      pIndex = pLoop->u.btree.pIndex;
+      nColumn = pIndex->nColumn;
+      if( pIndex->onError==OE_None ){
+        isUnique = 0;
+      }else if( (pLoop->wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_RANGE
+                                   |WHERE_COLUMN_NULL))!=0 ){
+        isUnique = 0;
+      }else if( pLoop->u.btree.nEq < pIndex->nColumn ){
+        isUnique = 0;
+      }
+    }
+    if( !isUnique && requireUnique ) return 0;
+    requireUnique = !isUnique;
+    iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
+    j = 0;
+    revSet = rev = 0;
+    for(j=0; j<nColumn && nUsed<nOrderBy; j++, nUsed++){
+      pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[nUsed].pExpr);
+      assert( pOBExpr->op==TK_COLUMN );
+      if( pOBExpr->iTable!=iCur ) break;
+      if( pIndex==0 ){
+        if( pOBExpr->iColumn<0 && j==0 ){
+          isUnique = 1;
+          rev = pOrderBy->a[nUsed].sortOrder;
+        }else if( isUnique ){
+          continue;
+        }else{
+          return 0;
+        }
+      }
+      if( isUnique ) continue;
+      iColumn = pIndex->aiColumn[j];
+      if( iColumn==pIndex->pTable->iPKey ) iColumn = -1;
+      if( pOBExpr->iColumn!=iColumn ) return 0;
+      pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[nUsed].pExpr);
+      if( !pColl ) pColl = db->pDfltColl;
+      if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) return 0;
+      if( revSet ){
+        if( pIndex->aSortOrder[j]!=rev ) return 0;
+      }else{
+        rev = pIndex->aSortOrder[j];
+        revSet = 1;
+      }
+    }
+    if( rev ) revMask |= ((Bitmask)1)<<i;
+  }
+  if( nUsed==nOrderBy ){
+    *pRevMask = revMask;
+    return 1;
+  }
+  return -1;
 }
 
 
@@ -5831,6 +5951,7 @@ static int wherePathSolver(WhereInfo *pWInfo){
     for(ii=0, pFrom=aFrom; ii<nFrom; ii++, pFrom++){
       for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
         Bitmask maskNew;
+        Bitmask revMask = 0;
         u8 isOrderedValid = pFrom->isOrderedValid;
         u8 isOrdered = pFrom->isOrdered;
         if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
@@ -5840,7 +5961,8 @@ static int wherePathSolver(WhereInfo *pWInfo){
         rCost = pWLoop->rSetup + pWLoop->rRun*pFrom->nRow + pFrom->rCost;
         maskNew = pFrom->maskLoop | pWLoop->maskSelf;
         if( !isOrderedValid ){
-          switch( wherePathSatisfiesOrderBy(pWInfo, pFrom, iLoop, pWLoop) ){
+          switch( wherePathSatisfiesOrderBy(pWInfo, pFrom, iLoop,
+                                            pWLoop, &revMask) ){
             case 1:  /* Yes.  pFrom+pWLoop does satisfy the ORDER BY clause */
               isOrdered = 1;
               isOrderedValid = 1;
@@ -5873,6 +5995,7 @@ static int wherePathSolver(WhereInfo *pWInfo){
         }
         /* pWLoop is a winner.  Add it to the set of best so far */
         pTo->maskLoop = pFrom->maskLoop | pWLoop->maskSelf;
+        pTo->revLoop = revMask;
         pTo->nRow = pFrom->nRow * pWLoop->nOut;
         pTo->rCost = rCost;
         pTo->isOrderedValid = isOrderedValid;
@@ -6193,7 +6316,12 @@ WhereInfo *sqlite3WhereBegin(
     && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
   if( sqlite3WhereTrace ){
     int ii;
-    sqlite3DebugPrintf("------------ Solution ----------------\n");
+    sqlite3DebugPrintf("------------ Solution -------------");
+    if( pWInfo->nOBSat ){
+      sqlite3DebugPrintf(" ORDER BY omitted\n");
+    }else{
+      sqlite3DebugPrintf("\n");
+    }
     for(ii=0; ii<nTabList; ii++){
       whereLoopPrint(pWInfo->a[ii].pWLoop, pTabList);
     }