]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
When looking for star-queries, do not count a table as a dimension table
authordrh <>
Fri, 24 Jan 2025 14:51:15 +0000 (14:51 +0000)
committerdrh <>
Fri, 24 Jan 2025 14:51:15 +0000 (14:51 +0000)
if that table is separated from the fact table by an OUTER or CROSS join
or if the table is a self-join.

FossilOrigin-Name: 5aebd7df0d577e98b3affd22b84b42dfe84a9f37fa29187505cc245b95460ba4

manifest
manifest.uuid
src/where.c

index 30ff3f1fd0c7a298d81620615e691b739801f413..04cbc622ad13a9ca770d9ec3ce41a508631ef8bc 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\spossible\sinfinity\sloop\sin\sdebugging-printf\slogic\sin\sthe\squery\splanner.\nNo\schanges\sto\sproduction\scode.
-D 2025-01-23T21:06:59.817
+C When\slooking\sfor\sstar-queries,\sdo\snot\scount\sa\stable\sas\sa\sdimension\stable\nif\sthat\stable\sis\sseparated\sfrom\sthe\sfact\stable\sby\san\sOUTER\sor\sCROSS\sjoin\nor\sif\sthe\stable\sis\sa\sself-join.
+D 2025-01-24T14:51:15.886
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d
@@ -863,7 +863,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c 4e6181d8780ab0af2e1388d0754cbe6f2f04593d2b1ab6c41699a89942fd8997
 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
 F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014
-F src/where.c 6722991bece039c7094e9e31bb57b9eb155299f01d2209f1378a81c9605ded31
+F src/where.c 5112b3f0a27bc9708b26b8dc0a513a38c3f03e58a3ad2a6323aa706d8459d7b7
 F src/whereInt.h 2b0804f300c7f65de4046a1d81c65f01b208d6c08950ccd1fa6b8c16162a8af7
 F src/wherecode.c 0c3d3199a2b769a5e2bb70feb5003dc85b3d86842ecaf903a47f2b4205ca5dab
 F src/whereexpr.c 0f93a29cabd3a338d09a1f5c6770620a1ac51ec1157f3229502a7e7767c60b6f
@@ -2208,8 +2208,11 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
 F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P c9dc581e0287e3462ac55f80ca76e7e98d31157022052c892517363c45287a7b
-R fa61ff8d8db6242b1ed2cb7c9f3ef7ec
+P 9a20b94080f5379867530772e081b737ae4cf6b416469dcabb85b8dd819f491f
+R fc36952f8e44ca6141073778b4516171
+T *branch * star-query-heuristic
+T *sym-star-query-heuristic *
+T -sym-trunk *
 U drh
-Z 2dbdf3625aeab02b475903def004e928
+Z d58ed75293e728cbf22986bd4a983f27
 # Remove this line to create a well-formed Fossil manifest.
index e686eed397f092c6d11f459e63d6a0efada9d14e..03fb6a033f121c1247308f54418671ab261ba819 100644 (file)
@@ -1 +1 @@
-9a20b94080f5379867530772e081b737ae4cf6b416469dcabb85b8dd819f491f
+5aebd7df0d577e98b3affd22b84b42dfe84a9f37fa29187505cc245b95460ba4
index cad66e70bcf0599dfc0908b85920cf23e5b76879..6327f7688e220eb4beb40dbc3ecd6c55d0080b0a 100644 (file)
@@ -5441,11 +5441,13 @@ static LogEst whereSortingCost(
 **     18    for star queries
 **     12    otherwise
 **
-** For the purposes of SQLite, a star-query is defined as a query
-** with a large central table that is joined (using an INNER JOIN,
-** not a LEFT JOIN) against four or more smaller tables.  The central
-** table is called the "fact" table.  The smaller tables that get
-** joined are "dimension tables".
+** For the purposes of this heuristic, a star-query is defined as a query
+** with a large central table that is joined using an INNER JOIN,
+** not CROSS or OUTER JOINs, against four or more smaller tables.
+*  The central table is called the "fact" table.  The smaller tables
+** that get joined are "dimension tables".  Also, any table that is
+** self-joined cannot be a dimension table; we assume that dimension
+** tables may only be joined against fact tables.
 **
 ** SIDE EFFECT:  (and really the whole point of this subroutine)
 **
@@ -5456,44 +5458,101 @@ static LogEst whereSortingCost(
 ** resulting in poor query plans.  The total amount of heuristic cost
 ** adjustment is stored in pWInfo->nOutStarDelta and the cost adjustment
 ** for each WhereLoop is stored in its rStarDelta field.
+**
+** This heuristic can be completely disabled, so that no query is
+** considered a star-query, using SQLITE_TESTCTRL_OPTIMIZATION to
+** disable the SQLITE_StarQuery optimization.  In the CLI, the command
+** to do that is:  ".testctrl opt -starquery".
 */
 static int computeMxChoice(WhereInfo *pWInfo, LogEst nRowEst){
   int nLoop = pWInfo->nLevel;    /* Number of terms in the join */
+  WhereLoop *pWLoop;             /* For looping over WhereLoops */
+
+#ifdef SQLITE_DEBUG
+  /* The star-query detection code below makes use of the following
+  ** properties of the WhereLoop list, so verifying them before
+  ** continuing:
+  **    (1)  .maskSelf is the bitmask corresponding to .iTab
+  **    (2)  The WhereLoop list is in ascending .iTab order
+  */
+  for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
+    assert( pWLoop->maskSelf==MASKBIT(pWLoop->iTab) );
+    assert( pWLoop->pNextLoop==0 || pWLoop->iTab<=pWLoop->pNextLoop->iTab );
+  }
+#endif /* SQLITE_DEBUG */
+
   if( nRowEst==0
    && nLoop>=5 
    && OptimizationEnabled(pWInfo->pParse->db, SQLITE_StarQuery)
   ){
+    SrcItem *aFromTabs;    /* All terms of the FROM clause */
+    int iFromIdx;          /* Term of FROM clause is the candidate fact-table */
+    Bitmask m;             /* Bitmask for candidate fact-table */
+    Bitmask mSelfJoin = 0; /* Tables that cannot be dimension tables */       
+    WhereLoop *pStart;     /* Where to start searching for dimension-tables */
+
     /* Check to see if we are dealing with a star schema and if so, reduce
     ** the cost of fact tables relative to dimension tables, as a heuristic
     ** to help keep the fact tables in outer loops.
     */
-    int iLoop;                /* Counter over join terms */
-    Bitmask m;                /* Bitmask for current loop */
     assert( pWInfo->nOutStarDelta==0 );
-    for(iLoop=0, m=1; iLoop<nLoop; iLoop++, m<<=1){
-      WhereLoop *pWLoop;        /* For looping over WhereLoops */
+    aFromTabs = pWInfo->pTabList->a;
+    pStart = pWInfo->pLoops;
+    for(iFromIdx=0, m=1; iFromIdx<nLoop; iFromIdx++, m<<=1){
       int nDep = 0;             /* Number of dimension tables */
       LogEst rDelta;            /* Heuristic cost adjustment */
       Bitmask mSeen = 0;        /* Mask of dimension tables */
-      for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
-        if( (pWLoop->prereq & m)!=0        /* pWInfo depends on iLoop */
+      SrcItem *pFactTab;        /* The candidate fact table */
+
+      pFactTab = aFromTabs + iFromIdx;
+      if( (pFactTab->fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){
+        /* If the candidate fact-table is the right table of an outer join
+        ** restrict the search for dimension-tables to be tables to the right
+        ** of the fact-table. */
+        if( iFromIdx+4 > nLoop ) break;  /* Impossible to reach nDep>=4 */
+        while( ALWAYS(pStart) && pStart->iTab<=iFromIdx ){
+          pStart = pStart->pNextLoop;
+        }
+      }
+      for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){
+        if( (aFromTabs[pWLoop->iTab].fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){
+          /* Fact-tables and dimension-tables cannot be separated by an
+          ** outer join (at least for the definition of fact- and dimension-
+          ** used by this heuristic). */
+          break;
+        }
+        if( (pWLoop->prereq & m)!=0        /* pWInfo depends on iFromIdx */
          && (pWLoop->maskSelf & mSeen)==0  /* pWInfo not already a dependency */
-         && (pWInfo->pTabList->a[pWLoop->iTab].fg.jointype & JT_LEFT)==0
-                                               /* ^- pWInfo isn't a LEFT JOIN */
+         && (pWLoop->maskSelf & mSelfJoin)==0 /* Not a self-join */
         ){
-          nDep++;
-          mSeen |= pWLoop->maskSelf;
+          if( aFromTabs[pWLoop->iTab].pSTab==pFactTab->pSTab ){
+            mSelfJoin |= m;
+          }else{
+            nDep++;
+            mSeen |= pWLoop->maskSelf;
+          }
         }
       }
       if( nDep<=3 ) continue;
       rDelta = 15*(nDep-3);
 #ifdef WHERETRACE_ENABLED /* 0x4 */
       if( sqlite3WhereTrace&0x4 ){
-         SrcItem *pItem = pWInfo->pTabList->a + iLoop;
-         sqlite3DebugPrintf(
-             "Fact-table %s(%d): %d dimensions, cost reduced %d\n",
-             pItem->zAlias ? pItem->zAlias : pItem->pSTab->zName, iLoop,
-             nDep, rDelta);
+        Bitmask x;
+        int ii;
+        sqlite3DebugPrintf(
+          "Fact-table %s(%d): cost reduced %d due to %d dimension tables:",
+          pFactTab->zAlias ? pFactTab->zAlias : pFactTab->pSTab->zName,
+          iFromIdx, rDelta, nDep
+        );
+        for(ii=0, x=1; ii<nLoop; ii++, x<<=1){
+          if( x & mSeen ){
+            SrcItem *pDim = aFromTabs + ii;
+            sqlite3DebugPrintf(" %s(%d)",
+              pDim->zAlias ? pDim->zAlias : pDim->pSTab->zName, ii
+            );
+          }
+        }
+        sqlite3DebugPrintf("\n");
       }
 #endif
       if( pWInfo->nOutStarDelta==0 ){