Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -1977,10 +1977,33 @@ iDestroyed = iLargest; } } #endif } + +/* +** Remove entries from the sqlite_stat1 and sqlite_stat2 tables +** after a DROP INDEX or DROP TABLE command. +*/ +static void sqlite3ClearStatTables( + Parse *pParse, /* The parsing context */ + int iDb, /* The database number */ + const char *zType, /* "idx" or "tbl" */ + const char *zName /* Name of index or table */ +){ + static const char *azStatTab[] = { "sqlite_stat1", "sqlite_stat2" }; + int i; + const char *zDbName = pParse->db->aDb[iDb].zName; + for(i=0; idb, azStatTab[i], zDbName) ){ + sqlite3NestedParse(pParse, + "DELETE FROM %Q.%s WHERE %s=%Q", + zDbName, azStatTab[i], zType, zName + ); + } + } +} /* ** This routine is called to do the work of a DROP TABLE statement. ** pName is the name of the table to be dropped. */ @@ -2117,18 +2140,11 @@ ** database. */ sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'", pDb->zName, SCHEMA_TABLE(iDb), pTab->zName); - - /* Drop any statistics from the sqlite_stat1 table, if it exists */ - if( sqlite3FindTable(db, "sqlite_stat1", db->aDb[iDb].zName) ){ - sqlite3NestedParse(pParse, - "DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q", pDb->zName, pTab->zName - ); - } - + sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName); if( !isView && !IsVirtual(pTab) ){ destroyTable(pParse, pTab); } /* Remove the table entry from SQLite's internal schema and modify @@ -2947,19 +2963,13 @@ v = sqlite3GetVdbe(pParse); if( v ){ sqlite3BeginWriteOperation(pParse, 1, iDb); sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE name=%Q AND type='index'", - db->aDb[iDb].zName, SCHEMA_TABLE(iDb), - pIndex->zName - ); - if( sqlite3FindTable(db, "sqlite_stat1", db->aDb[iDb].zName) ){ - sqlite3NestedParse(pParse, - "DELETE FROM %Q.sqlite_stat1 WHERE idx=%Q", - db->aDb[iDb].zName, pIndex->zName - ); - } + db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName + ); + sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName); sqlite3ChangeCookie(pParse, iDb); destroyRootPage(pParse, pIndex->tnum, iDb); sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0); } Index: test/analyze.test ================================================================== --- test/analyze.test +++ test/analyze.test @@ -284,10 +284,68 @@ sqlite3 db test.db execsql { SELECT * FROM t4 WHERE x=1234; } } {} + +# Verify that DROP TABLE and DROP INDEX remove entries from the +# sqlite_stat1 and sqlite_stat2 tables. +# +do_test analyze-5.0 { + execsql { + DELETE FROM t3; + DELETE FROM t4; + INSERT INTO t3 VALUES(1,2,3,4); + INSERT INTO t3 VALUES(5,6,7,8); + INSERT INTO t3 SELECT a+8, b+8, c+8, d+8 FROM t3; + INSERT INTO t3 SELECT a+16, b+16, c+16, d+16 FROM t3; + INSERT INTO t3 SELECT a+32, b+32, c+32, d+32 FROM t3; + INSERT INTO t3 SELECT a+64, b+64, c+64, d+64 FROM t3; + INSERT INTO t4 SELECT a, b, c FROM t3; + ANALYZE; + SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1; + SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1; + } +} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4} +ifcapable stat2 { + do_test analyze-5.1 { + execsql { + SELECT DISTINCT idx FROM sqlite_stat2 ORDER BY 1; + SELECT DISTINCT tbl FROM sqlite_stat2 ORDER BY 1; + } + } {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4} +} +do_test analyze-5.2 { + execsql { + DROP INDEX t3i2; + SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1; + SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1; + } +} {t3i1 t3i3 t4i1 t4i2 t3 t4} +ifcapable stat2 { + do_test analyze-5.3 { + execsql { + SELECT DISTINCT idx FROM sqlite_stat2 ORDER BY 1; + SELECT DISTINCT tbl FROM sqlite_stat2 ORDER BY 1; + } + } {t3i1 t3i3 t4i1 t4i2 t3 t4} +} +do_test analyze-5.4 { + execsql { + DROP TABLE t3; + SELECT DISTINCT idx FROM sqlite_stat1 ORDER BY 1; + SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1; + } +} {t4i1 t4i2 t4} +ifcapable stat2 { + do_test analyze-5.5 { + execsql { + SELECT DISTINCT idx FROM sqlite_stat2 ORDER BY 1; + SELECT DISTINCT tbl FROM sqlite_stat2 ORDER BY 1; + } + } {t4i1 t4i2 t4} +} # This test corrupts the database file so it must be the last test # in the series. # do_test analyze-99.1 {