]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
If a table has one or more rows and it has a partial index has zero rows, optimize-after-shrink
authordrh <>
Mon, 19 Feb 2024 16:22:58 +0000 (16:22 +0000)
committerdrh <>
Mon, 19 Feb 2024 16:22:58 +0000 (16:22 +0000)
still make an entry in the sqlite_stat1 table for the partial index, so that
we know that "PRAGMA optimize" does not need to redo the whole table.

FossilOrigin-Name: e147b18991dd462fff367442acb0504fdf193a31843ed34ec8c1ced30747bf8a

ext/session/sessionstat1.test
manifest
manifest.uuid
src/analyze.c
src/btree.c
src/pragma.c
src/vdbe.c

index 2757d60440dc1e7de5cb202273402a8fa5ef881b..16dc4e2727f95aa734a391fcfd273c44496fa847 100644 (file)
@@ -92,7 +92,7 @@ do_test 2.1 {
 } {}
 
 do_execsql_test -db db2 2.2 {
-  SELECT * FROM sqlite_stat1
+  SELECT * FROM sqlite_stat1 ORDER BY tbl, idx
 } {
   t1 sqlite_autoindex_t1_1 {32 1} 
   t1 t1b {32 4} 
@@ -104,7 +104,7 @@ do_test 2.3 {
 } {}
 
 do_execsql_test -db db2 2.4 {
-  SELECT * FROM sqlite_stat1
+  SELECT * FROM sqlite_stat1 ORDER BY tbl, idx;
 } {
   t1 sqlite_autoindex_t1_1 {32 1} 
   t1 t1b {32 4} 
index fdb16737f154dad3f393f1e1bc20c3e75637a712..c12c473dca73e4d71fac24196776c8e689f93024 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Simplifications\sto\sPRAGMA\soptimize\sto\smake\sit\seasier\sto\suse.\s\sIt\salways\ntries\sto\sANALYZE\sunanalyzed\sindexes.\s\sThe\s0x10000\sflag\sjust\smakes\sit\scheck\nfor\ssize\schanges\sin\sall\stables.
-D 2024-02-19T13:50:09.987
+C If\sa\stable\shas\sone\sor\smore\srows\sand\sit\shas\sa\spartial\sindex\shas\szero\srows,\nstill\smake\san\sentry\sin\sthe\ssqlite_stat1\stable\sfor\sthe\spartial\sindex,\sso\sthat\nwe\sknow\sthat\s"PRAGMA\soptimize"\sdoes\snot\sneed\sto\sredo\sthe\swhole\stable.
+D 2024-02-19T16:22:58.040
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -567,7 +567,7 @@ F ext/session/sessionnoop2.test de4672dce88464396ec9f30ed08c6c01643a69c53ae540fa
 F ext/session/sessionrebase.test 702378bdcb5062f1106e74457beca8797d09c113a81768734a58b197b5b334e2
 F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a80600a44396f7363
 F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
-F ext/session/sessionstat1.test b039e38e2ba83767b464baf39b297cc0b1cc6f3292255cb467ea7e12d0d0280c
+F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec
 F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
 F ext/session/sqlite3session.c 829d468f0f3d2710aace56b0116a7ca3f414683ce78e3125ae5e21547a895078
 F ext/session/sqlite3session.h 4cf19a51975746d7cff2fdd74db8b769c570958e1c3639ac150d824ac1553b3e
@@ -672,13 +672,13 @@ F sqlite3.1 acdff36db796e2d00225b911d3047d580cd136547298435426ce9d40347973cc
 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
 F sqlite_cfg.h.in baf2e409c63d4e7a765e17769b6ff17c5a82bbd9cbf1e284fd2e4cefaff3fcf2
 F src/alter.c 30c2333b8bb3af71e4eb9adeadee8aa20edb15917ed44b8422e5cd15f3dfcddc
-F src/analyze.c 0f15753308c3bca7674f31fa7e0807ffcb8b120c36eef7d00b62b33079ddc854
+F src/analyze.c dacc8f062bbda02c8ed3bbca0ab6122de010d094101d568df266a7b0d665d74f
 F src/attach.c cc9d00d30da916ff656038211410ccf04ed784b7564639b9b61d1839ed69fd39
 F src/auth.c 19b7ccacae3dfba23fc6f1d0af68134fa216e9040e53b0681b4715445ea030b4
 F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
 F src/bitvec.c 9eac5f42c11914d5ef00a75605bb205e934f435c579687f985f1f8b0995c8645
 F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522
-F src/btree.c dfd351decf9f819393dae8fb7294cae69c3818069d27f47fee34d5b505e37b47
+F src/btree.c 2f9dcf7c27879edb7897354029343bcab0c5a3dd2bde3bf3fd8f814b71344d22
 F src/btree.h 03e3356f5208bcab8eed4e094240fdac4a7f9f5ddf5e91045ce589f67d47c240
 F src/btreeInt.h 3e2589726c4f105e653461814f65857465da68be1fac688de340c43b873f4062
 F src/build.c 04f1bcee189f045ab086d84fee95db42cb49df82ff8e84af8136309ff3c8a75f
@@ -732,7 +732,7 @@ F src/parse.y bfd6da46fc895cd8237400ff485d04ab0b32e47eb56de20982bb7f53e56c1f42
 F src/pcache.c 040b165f30622a21b7a9a77c6f2e4877a32fb7f22d4c7f0d2a6fa6833a156a75
 F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
 F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00
-F src/pragma.c 9197331dd283a6010bb4e2c129a9015f854203d27fa6fbe89e1aea742b78351c
+F src/pragma.c 0bd3737acf69282fb1733e170a88a5caf3dba088092665e603ee5a6d651c0e2f
 F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
 F src/prepare.c 371f6115cb69286ebc12c6f2d7511279c2e47d9f54f475d46a554d687a3b312c
 F src/printf.c 18fbdf028345c8fbe6044f5f5bfda5a10d48d6287afef088cc21b0ca57985640
@@ -809,7 +809,7 @@ F src/upsert.c fa125a8d3410ce9a97b02cb50f7ae68a2476c405c76aa692d3acf6b8586e9242
 F src/utf.c f23165685a67b4caf8ec08fb274cb3f319103decfb2a980b7cfd55d18dfa855e
 F src/util.c 078f040366d5bd5f47658d045f901c768c1c636c6eaea121f3a1cbd63c3edb5b
 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104
-F src/vdbe.c 6d2aecc9ab2f9db557b4841775d016e2697ff6312242e9a492cdf3e2529dffa6
+F src/vdbe.c 805af8bec9cae76a873b0ec4c91c3d1bd49b38e86c1c533ed87ec7a07a5042a5
 F src/vdbe.h 88e19a982df9027ec1c177c793d1a5d34dc23d8f06e3b2d997f43688b05ee0eb
 F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c
 F src/vdbeapi.c 8f57d60c89da0b60e6d4e272358c511f6bae4e24330bdb11f8b42f986d1bf21b
@@ -2162,8 +2162,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 4abd47b5917099a2f74e53e12c987da0722304a5e9a93b6d43015c1f45c48444
-R 39db09ba2f5384fee677814cc2cbe4fe
+P 44ed7f4cd07a88a2fdd303a2c78e6babe01d7344b399bd2b80ed68d75a77aaa2
+R 425f4513952a4b2a7a3cf3f40b480a46
 U drh
-Z cf23276671f3baaf4e4f3579488af586
+Z 08209902059a589603329b04ece67a70
 # Remove this line to create a well-formed Fossil manifest.
index f1cc73e3f05307327dd33a011ff681c9daf34553..5ae06f96c4eda00c6171b3af816570d7740aa467 100644 (file)
@@ -1 +1 @@
-44ed7f4cd07a88a2fdd303a2c78e6babe01d7344b399bd2b80ed68d75a77aaa2
\ No newline at end of file
+e147b18991dd462fff367442acb0504fdf193a31843ed34ec8c1ced30747bf8a
\ No newline at end of file
index 59e3d9837731f379f6610170af1c52f4f7d53919..e7c1068ae080ccb30a608dba1a6812994d5e78e7 100644 (file)
@@ -872,7 +872,7 @@ static void statGet(
       if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1;
       sqlite3_str_appendf(&sStat, " %llu", iVal);
 #ifdef SQLITE_ENABLE_STAT4
-      assert( p->current.anEq[i] );
+      assert( p->current.anEq[i] || p->nRow==0 );
 #endif
     }
     sqlite3ResultStrAccum(context, &sStat);
@@ -1057,7 +1057,7 @@ static void analyzeOneTable(
 
   for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
     int nCol;                     /* Number of columns in pIdx. "N" */
-    int addrRewind;               /* Address of "OP_Rewind iIdxCur" */
+    int addrGotoEnd;               /* Address of "OP_Rewind iIdxCur" */
     int addrNextRow;              /* Address of "next_row:" */
     const char *zIdxName;         /* Name of the index */
     int nColTest;                 /* Number of columns to test for changes */
@@ -1081,9 +1081,14 @@ static void analyzeOneTable(
     /*
     ** Pseudo-code for loop that calls stat_push():
     **
-    **   Rewind csr
-    **   if eof(csr) goto end_of_scan;
     **   regChng = 0
+    **   Rewind csr
+    **   if eof(csr){
+    **      stat_init() with count = 0;
+    **      goto end_of_scan;
+    **   }
+    **   count()
+    **   stat_init()
     **   goto chng_addr_0;
     **
     **  next_row:
@@ -1122,41 +1127,36 @@ static void analyzeOneTable(
     sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
     VdbeComment((v, "%s", pIdx->zName));
 
-    /* Invoke the stat_init() function. The arguments are:
-    ** 
+    /* Implementation of the following:
+    **
+    **   regChng = 0
+    **   Rewind csr
+    **   if eof(csr){
+    **      stat_init() with count = 0;
+    **      goto end_of_scan;
+    **   }
+    **   count()
+    **   stat_init()
+    **   goto chng_addr_0;
+    */
+    assert( regTemp2==regStat+4 );
+    sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
+
+    /* Arguments to stat_init(): 
     **    (1) the number of columns in the index including the rowid
     **        (or for a WITHOUT ROWID table, the number of PK columns),
     **    (2) the number of columns in the key without the rowid/pk
-    **    (3) estimated number of rows in the index,
-    */
+    **    (3) estimated number of rows in the index. */
     sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1);
     assert( regRowid==regStat+2 );
     sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid);
-#ifdef SQLITE_ENABLE_STAT4
-    if( OptimizationEnabled(db, SQLITE_Stat4) ){
-      sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp);
-      addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
-      VdbeCoverage(v);
-    }else
-#endif
-    {
-      addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
-      VdbeCoverage(v);
-      sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1);
-    }
-    assert( regTemp2==regStat+4 );
-    sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
+    sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp,
+                      OptimizationDisabled(db, SQLITE_Stat4));
     sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4,
                                &statInitFuncdef, 0);
+    addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
+    VdbeCoverage(v);
 
-    /* Implementation of the following:
-    **
-    **   Rewind csr
-    **   if eof(csr) goto end_of_scan;
-    **   regChng = 0
-    **   goto next_push_0;
-    **
-    */
     sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
     addrNextRow = sqlite3VdbeCurrentAddr(v);
 
@@ -1263,6 +1263,12 @@ static void analyzeOneTable(
     }
 
     /* Add the entry to the stat1 table. */
+    if( pIdx->pPartIdxWhere ){
+      /* Partial indexes might get a zero-entry in sqlite_stat1.  But
+      ** an empty table is omitted from sqlite_stat1. */
+      sqlite3VdbeJumpHere(v, addrGotoEnd);
+      addrGotoEnd = 0;
+    }
     callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1);
     assert( "BBB"[0]==SQLITE_AFF_TEXT );
     sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
@@ -1286,6 +1292,12 @@ static void analyzeOneTable(
       int addrIsNull;
       u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
 
+      /* No STAT4 data is generated if the number of rows is zero */
+      if( addrGotoEnd==0 ){
+        sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER);
+        addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1);
+      }
+
       if( doOnce ){
         int mxCol = nCol;
         Index *pX;
@@ -1338,7 +1350,7 @@ static void analyzeOneTable(
 #endif /* SQLITE_ENABLE_STAT4 */
 
     /* End of analysis */
-    sqlite3VdbeJumpHere(v, addrRewind);
+    if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd);
   }
 
 
index 49c07c81a6f3b55da5ce782873881671bf19dec5..16b683abe7d8794ac941b6db3f7ec61cdafd4e95 100644 (file)
@@ -6182,10 +6182,10 @@ i64 sqlite3BtreeRowCountEst(BtCursor *pCur){
   assert( cursorOwnsBtShared(pCur) );
   assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
 
-  /* Currently this interface is only called by the OP_IfSmaller
-  ** opcode, and it that case the cursor will always be valid and
-  ** will always point to a leaf node. */
-  if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1;
+  /* Currently this interface is only called by the OP_IfSizeBetween
+  ** opcode and the OP_Count opcode with P3=1.  In either case,
+  ** the cursor will always be valid unless the btree is empty. */
+  if( pCur->eState!=CURSOR_VALID ) return 0;
   if( NEVER(pCur->pPage->leaf==0) ) return -1;
 
   n = pCur->pPage->nCell;
index 53dc9ced30485cf548d43e84a302df1859f0ced4..77ec9a57e224d06fabba50db7a23304124c692c9 100644 (file)
@@ -2427,9 +2427,8 @@ void sqlite3Pragma(
   **               database will usually be less than 100 milliseconds on
   **               a RaspberryPI-4 class machine.  On by default.
   **
-  **    0x00020    Run ANALYZE on any table that has a complete index
-  **               (an index without a WHERE clause) that lacks an entry
-  **               in the sqlite_stat1 table.  On by default.
+  **    0x00020    Run ANALYZE on any table that has a index that lacks an
+  **               entry in the sqlite_stat1 table.  On by default.
   **
   **    0x10000    Look at tables to see if they need to be reanalyzed
   **               due to growth or shrinkage even if they have not been
@@ -2454,14 +2453,14 @@ void sqlite3Pragma(
   **
   ** (4) One or more of the following is true:
   **      (4a) The 0x10000 MASK bit is set.
-  **      (4b) One or more complete indexes on the table lacks an entry
+  **      (4b) One or more indexes on the table lacks an entry
   **           in the sqlite_stat1 table.
   **      (4c) The query planner used sqlite_stat1-style statistics for one
   **           or more indexes of the tableat some point during the lifetime
   **           of the current connection.
   **
   ** (5) One or more of the following is true:
-  **      (5a) One or mroe complete indexes on the table lacks an entry
+  **      (5a) One or mroe indexes on the table lacks an entry
   **           in the sqlite_stat1 table.  (Same as 4a)
   **      (5b) The number of rows in the table has increased or decreased by
   **           10-fold.  In other words, the current size of the table is
@@ -2515,12 +2514,12 @@ void sqlite3Pragma(
         if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ) continue;
 
         /* Find the size of the table as last recorded in sqlite_stat1.
-        ** If any complete index (index without a WHERE clause) is unanalyzed,
-        ** then the threshold is -1 to indicate a new, unanalyzed index
+        ** If any index is unanalyzed, then the threshold is -1 to
+        ** indicate a new, unanalyzed index
         */
         szThreshold = pTab->nRowLogEst;
         for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
-          if( !pIdx->hasStat1 && pIdx->pPartIdxWhere==0 ){
+          if( !pIdx->hasStat1 ){
             szThreshold = -1; /* Always analyze if any index lacks statistics */
             break;
           }
@@ -2534,7 +2533,7 @@ void sqlite3Pragma(
         }else if( opMask & 0x10000 ){
           /* Check for size change if 0x10000 is set */
         }else if( pTab->pIndex!=0 && szThreshold<0 ){
-          /* Do analysis if unanalyzed complete indexes exists */
+          /* Do analysis if unanalyzed indexes exists */
         }else{
           /* Otherwise, we can skip this table */
           continue;
@@ -2549,7 +2548,7 @@ void sqlite3Pragma(
 
         /* Reanalyze if the table is 10 times larger or smaller than
         ** the last analysis.  Unconditional reanalysis if there are
-        ** unanalyzed complete indexes. */
+        ** unanalyzed indexes. */
         if( szThreshold>=0 ){
           LogEst iRange = 33;   /* 10x size change */
           sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
index 3b52c13fa74a27a7644a7fe291c25c4038f339b9..48a029dbf87c6b4285f488c0a68a29c1a8697633 100644 (file)
@@ -2086,7 +2086,7 @@ case OP_RealAffinity: {                  /* in1 */
 }
 #endif
 
-#ifndef SQLITE_OMIT_CAST
+#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_ANALYZE)
 /* Opcode: Cast P1 P2 * * *
 ** Synopsis: affinity(r[P1])
 **