aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <>2024-02-19 16:22:58 +0000
committerdrh <>2024-02-19 16:22:58 +0000
commite7bdb2172c717f7ba64e15fb65b930524e759d71 (patch)
tree4df3b8b0a318608261e4f2f2ec8ae120d7508e59 /src
parent6c6356f7f2f3c6e717d445d117407e2edecccac5 (diff)
downloadsqlite-e7bdb2172c717f7ba64e15fb65b930524e759d71.tar.gz
sqlite-e7bdb2172c717f7ba64e15fb65b930524e759d71.zip
If a table has one or more rows and it has a partial index has zero rows,
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
Diffstat (limited to 'src')
-rw-r--r--src/analyze.c74
-rw-r--r--src/btree.c8
-rw-r--r--src/pragma.c19
-rw-r--r--src/vdbe.c2
4 files changed, 57 insertions, 46 deletions
diff --git a/src/analyze.c b/src/analyze.c
index 59e3d9837..e7c1068ae 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -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);
}
diff --git a/src/btree.c b/src/btree.c
index 49c07c81a..16b683abe 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -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;
diff --git a/src/pragma.c b/src/pragma.c
index 53dc9ced3..77ec9a57e 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -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);
diff --git a/src/vdbe.c b/src/vdbe.c
index 3b52c13fa..48a029dbf 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -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])
**